Saturday, October 08, 2016

DTA'S-Developers Turned Analysts

DTA’s – Developers-Turned-Analysts


Background 

I was a developer (by title) for 15 years before I got my first position as an Analyst.  I was warned that being an Analyst meant that I didn't have to program and that I had to change my mindset from the technical side to the business side.  I thought this would be an easy transition, however, it proved to be anything but.  My first Detailed Functional Requirements (DFR) document was full of technical jargon and implementation language.  Another analyst pulled me aside and gave me the same advice I recently had to give a new developer-turned-analyst, heretofore known as DTA's.  The DTA asked me to review his first DFR and to give him some advice. 

The DFR 

To be fair his document was more technical in nature than your typical DFR because it was detailing a process that involved more than one program on more than one platform.  I decided to use the document as a teaching tool for him by also giving him a copy of Ivy Hooks "Writing Good Requirements" article from 1993.  I then tied in the recommendations that I made to him to that document as much as possible. 


Abstracting from Technical to Business Level

When I first started writing requirements I did the same thing, including technical info instead of abstracting to a higher more ‘business’ language level.  The thing to remember is that this document (and all ‘Requirements’ documents) is a ‘contract’ between the business and the developer.  If we put in our ‘contract’ that a developer utilize COBOL (for instance) then the developer’s hands are tied and he must use COBOL.  That is why we tend not to use implementation language in requirements.  This gives the developer the freedom to use whatever he wants (COBOL, Java, etc).

Thinking in terms of Testing

Another thing to keep in mind as an Analyst is that testers will be reviewing our requirements to determine if the program is meeting the ‘terms of the contract’ so to speak.  So the requirements also have to be testable.  When I think about whether a requirement I wrote is testable, I think, ‘Yes or No’.  This means can the result of this requirement results in a ‘Yes, it worked’ or ‘No, it didn’t work’ answer.  Some of the suggestions I made in the document will help drive this home.  I then reworded some of his requirements to make them testable.  For example, he had a requirement that stated: “Set and export a variable called ORACLE_HOME.”  I suggested the following: It might be easier to specify what the value of the variable is, rather than its name.  Also, with this being a requirement, you are telling the programmer that he has to name the variable ‘ORACLE_HOME’.  Remember, requirements are like a contract between the business and the developer.  Ask yourself, what does ‘ORACLE_HOME’ contain?  Then I suggested that he try saying something like “The system shall set the environment name”.

The advice was well-received and my manager asked that I save it to our team drive for other new analysts to refer to.  Transitioning from Developer to Analyst can be difficult.  It is not just giving up the coding part of your job, it is having to think at a less technical level and still conveying your message, not always an easy job.


Saturday, September 24, 2016

SharePoint Document Locator


Document Locator (DocLoc)



Background


I'd like to apologize for the format of this blog entry. Google has the worst blog editor in the history of blog editors, so this looks like crap.

I created a SharePoint webpart page that our team used to house project documents so that anyone could come to the site, access the page and retrieve all the documents related to that project.  All of our projects had the same artifacts so I created hyperlink fields for each artifact for each project and the result was this:

Our team was re-purposed and my former boss became a project manager, but she wanted a website, similar to the one I created for our former team that would allow people who are in different groups to access only the documents that they are allowed to see.

Requirements
Each project will have a SharePoint site dedicated to it.
There will be multiple groups.
o One group will be called ‘Sponsors’
o The other will be called ‘Quality Reviewers’.
Each group is mutually exclusive of the other and should only see documents that are for their groups.
Solution
I set up two permission groups, one called ‘Sponsors’ the other called ‘QR’ (lessening the chance of misspellings).
I utilized JavaScript, SPServices and JQuery to produce a page that was not ‘SharePointy’.
I created a folder content type that included a description

Thus you have something like this:
Each folder holding only the documents meant for that group.
Sponsor Folder


Quality Review Folder


Upon entering the application, the user’s group affiliation is determined:


The code searches the responseXML for the Group element and Name attribute:
"<Group xmlns=\"http://schemas.microsoft.com/sharepoint/soap/directory/\" ID=\"100737\" Name=\"QR\" Description=\"\" OwnerID=\"9742\" OwnerIsUser=\"True\"/>"


They are then taken to the folder that has the documents for that group:

Note: I used QueryOptions instead of CAML in order to achieve this.

Having set myself up as a ‘Quality Reviewer’, I receive the following results when I run the code:

Rounded Border
To achieve the rounded border, I added the following:
At the Top: <!DOCTYPE HTML>
In the Meta: <meta http-equiv="X-UA-Compatible" content="IE=9,chrome=1"/>
In the code:
o In my “floater” class I used border-radius: 25px to get the rounded edge
The Full Monty
Note: Interesting stuff is highlighted.
<!DOCTYPE HTML>
<html dir="ltr">

<head runat="server">
<meta name="WebPartPageExpansion" content="full" />
<meta http-equiv="Content-Language" content="en-us">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9,chrome=1"/>
<title>MI Doc Loc</title>
<!--JQuery UI CSS -->
<link rel="stylesheet" type="text/css" href="http://yoursite/jquery-ui.custom.css" />
<!--J Q U E R Y -->
<script type="text/javascript" src="http://yoursite/jquery-1.7.2.min.js">
</script>
<!-- S P  S E R V I C E S -->
<script type="text/javascript" src="http://yoursite/jquery.SPServices-0.7.1a.min.js">
</script>
<!--J Q U E R Y   U I -->
<script type="text/javascript"
src="http://yoursite/jquery-ui-1.8.21.custom.min.js">
</script>
<style type="text/css">
.bigLetter { color:gray; letter-spacing:10px; font-family:Arial, Helvetica, sans-serif; display:none; font-size:medium}
.myHeader { color:blue; letter-spacing:5px; font-family:Arial, Helvetica, sans-serif; font-size:large}
.instructions {
color:blue; font-family:Arial, Helvetica, sans-serif; font-size:x-small
}
.word { list-style-image:url('http://yoursite/mime_doc.png');}
.gen { list-style-image:url('http://yoursite/form_edit.png');}
.xl { list-style-image:url('http://yoursite/mime_xls.png');}
.pdf { list-style-image:url('http://yoursite/mime_pdf.png');}
.topmarg { margin:50px }
.floater {font-family:Arial, Helvetica, sans-serif; border: 2px solid orange;
    padding: 10px 40px; border-radius: 25px; display:none; width:500px}
a:link {text-decoration:none;}
a:visited {text-decoration:none;}
a:hover {text-decoration:none;}
a:active {text-decoration:none;}
</style>
</head>

<body>
<form id="form1" runat="server">
<!--Header--><div id="HEADER" class="bigLetter"></div>
<br>
<div class="myHeader">Project Document Locator</div>
<div id="instruct" class="instructions">
</div>
<div id="floatMe" class="floater">
<div class="ui-state-hover">Document(s)</div>
<ul id="listme"></ul>
</div>
<script type="text/javascript">
/////////////////////////// G L O B A L    V A R I A B L E S ///////////////////////////////////////
thisUserGroup="";
thissite="";
docarray=new Array();
docarr="";
howmany=0;
tabData = new Object();
dq = String.fromCharCode(34);
tab = String.fromCharCode(11);
////////////////////////////// C O D E /////////////////////////////////////////////////////////////
jQuery(document).ready(function($) { //READY
   //
   var thisUserName = $().SPServices.SPGetCurrentUser({
fieldName: "Title",
debug: false
});
ShowWelcome(thisUserName);
});// End READY
/********** Show the Welcome *************/
function ShowWelcome(thisUserName)
{
//Determine the users group affiliation Sponsor or Quality Reviewer
$().SPServices({
operation: "GetGroupCollectionFromUser",
userLoginName: $().SPServices.SPGetCurrentUser(),
async: false,
completefunc: function(xData, Status)
{
       if($(xData.responseXML).find("Group[Name='Sponsors']").length == 1)
       {
         alert("You are a Sponsor");
         tabData.Group = "Sponsor";
         tabData.Folder = "Sponsor";
       }
         if($(xData.responseXML).find("Group[Name='QR']").length == 1)
         {
         alert("You are a Quality Reviewer");
         tabData.Group = "QR";
         tabData.Folder = "Quality Review";
       }
 $("#HEADER").append("<p>Welcome, " + thisUserName + "</p><p>" + tabData.Folder + " Documents</p>");
 $("#HEADER").show();
 $("#instruct").show();
   }
  });
GetDocs();
}
//======================================================================================================
function GetDocs()
{//--Begin Function
       //Get Site URL
       thissite = $().SPServices.SPGetCurrentSite();

       //The Web Service method we are calling, to read list items we use 'GetListItems'
       var method = "GetListItems";
     
       //Supply the location and name of the list we are reading data from
       var myWebURL = thissite;
       var list = tabData.Folder;

       //We need to identify the fields we want to return.
                var fieldsToRead = "<ViewFields>" +
                                "<FieldRef Name='Title' />" +
                                "<FieldRef Name='EncodedAbsUrl' />" +
                                "<FieldRef Name='FileLeafRef' />" +
                                "<FieldRef Name='Desc' />" +
                            "</ViewFields>";
var QO = "<QueryOptions>" +
"<ViewAttributes Scope='FilesOnly' />" +
"<Folder>" +
thissite + "/" + tabData.Folder + "/" +
"</Folder>" +
"</QueryOptions>"
        //Here is our SPServices Call where we pass in the variables that we set above
        //This is where we show the Folder
        $().SPServices({
        debug: true,
                operation: method,
                async: false,
                webURL: myWebURL,
                listName: list,
                CAMLViewFields: fieldsToRead,
                CAMLQueryOptions: QO,
                completefunc: function (xData, Status)
                {
                //alert(".SPServices. Status =" + Status);
                //alert("Query Options: " + QO);
                var showmestuff = $().SPServices.SPDebugXMLHttpResult({ node:xData.responseXML });
                $(xData.responseXML).SPFilterNode("rs:data").each(function()
                {
                howmany = ($(this).attr("ItemCount"));
                if (howmany==0)
                {
                docarray[1]="No available " + tabData.Folder + " documents found for this project";
                        AddRowNoData(docarray);
                        }
                });
                    //Uncomment the 2 lines below to show what is returned by the web service
                    //$("#debugMe").append(showmestuff);
                   //$("#debugMe").show("slow");
                        //this code iterates through every row of data returned from the web service call
                        $(xData.responseXML).SPFilterNode("z:row").each(function()
                       {                          
                            //get the title URL for the sub-folders
                            tabData.url = ($(this).attr("ows_EncodedAbsUrl"));
                            //get the Document Titles
                            tabData.title = ($(this).attr("ows_Title"));
                            //get the Document Names
                            tabData.doc = ($(this).attr("ows_FileLeafRef"));
                            //get the document icons
                            splitext = tabData.doc.split(".");
                            switch(splitext[1])
                            {
                            case "docx":
                            case "dotx":
                            case "doc":
                            tabData.icon = "word";
                            break;
                            case "xls":
                            case "xlsx":
                            tabData.icon = "xl";
                            break;
                              case "pdf":
                            tabData.icon = "pdf";
                            break;
                          default:
                            tabData.icon = "gen";
                            }
                            //get the Document Description
                            tabData.Desc = ($(this).attr("ows_Desc"));
                            if (tabData.doc === undefined)
{
tabData.doc = nodoc;                  
}
                   docarr = tabData.doc;
                   docarray = docarr.split("#");
                            //add the data from the row to the table on the screen
                        AddRowToTable(tabData, docarray);
                      });//End SPFilerNode
var link2goback = "<a href=" + thissite + ">Return to SharePoint</a>";
$("#goHome").append(link2goback);                  
                   }//End  CompleteFunct
               });//--End SPServices
   } //End GetTopics
//=======================================================================================================
function AddRowToTable(tabData, docarray)
{
{
var showemall = "<li class=" + dq + tabData.icon + dq + ">" + tab + "<a href='" + tabData.url + "'>" + tabData.title + "</a></li>";
}
$("#listme").append(showemall);
$("#floatMe").show("slow");
}
function AddRowNoData(docarray)
{
{
var showemall = "<tr id=data><td colspan='2'>" + docarray[1] + "</td><td></td></tr>";
}
$("#floatMe").append(showemall);
$("#floatMe").show("slow");
}

</script>
&nbsp;
<div id="goHome"></div>
<div id="debugMe">
</div>
<br>
</form>
</body>

</html>