Source: site.view [edit]
Function name: analyzeReviews
Arguments: url
Description:
Page type: webl
Render function:   displayReviewAnalysis
Module: sandbox

Page source:

/*
 <status id="10000" iconUrl="https://reviews-prd.bixbydevelopers.com/images/icons/statuses/open.png" description="The issue is open and ready for the assignee to start work on it.">
                Review Requested
            </status>
            
            <statusCategory id="2" key="new" colorName="blue-gray"/>
            
            <resolution id="-1">
                Unresolved
            </resolution>
            
            <assignee username="-1">
                Unassigned
            </assignee>
            
            <created>
                Fri, 5 Jul 2019 10:31:35 +0000
            </created>
            
            <updated>
                Tue, 9 Jul 2019 21:35:28 +0000
            </updated>
            
*/

/* 
{"theo", "sehwa.p", "j1 pan", "Unassigned", "Promeet Mansata", "john alioto", "min.cheng@samsung.com", "ys33 yoon", 
"Andy", "shahnawaz mohammed", "jared lodwick", "geoff.wong", "alex ren", "kelly li", "roger kibbe", "daphne seo", 
"ameya b", "michael cuff", "Leena Malaviya"}
*/

var startTimeStr = "";
var endTimeStr = "";
var startTimeC;
var endTimeC;
var minTime = 0;
var maxTime = 0;


var SplitByTeam = fun(reviews)
   var kr = [];
   var us = [];
   every r in reviews do
     if (r.assignee == "ys33 yoon") or (r.assignee == "daphne seo") then
        kr = kr + [r]
     else
        us = us + [r]
     end
   end;
   return [. kr = kr, us = us .]
end;
  
var GetTimeSlice = fun(reviews, startMillis, endMillis)
   var res = [];
   every r in reviews do
     if (r.created > startMillis and r.created < endMillis) then
        res = res + [r]
     end
   end;
   return res
end;  

var GetField = fun (P, field, item)
   var E = Elem(P, field) inside item;
   if Size(E) == 1 then
      return Str_Trim(Text(E[0]))
   else
      return nil
   end
end;

var GetTimeObj = fun(s)
   // Fri, 5 Jul 2019 10:31:35 +0000
   var m = Str_Match(s, `\w+, (\d+) (\w+) (\d+) (\d+)\:(\d+)\:(\d+) \+0000`);
   var c = Wub_GetCalendarClass();

   var months = [. "Jan"=0, "Feb"=1, "Mar"=2, "Apr"=3, "May"=4, "Jun"=5, "Jul"=6, "Aug"=7, "Sep"=8, "Oct"=9, "Nov"=10, "Dec" = 11 .];

   var MONTH = 2;
   var DAYOFMONTH = 5;
   var YEAR = 1;
   var HOUROFDAY = 11;
   var MINUTE = 12;
   var SECOND = 13;
   c.set(YEAR, ToInt(m[3]));
   c.set(MONTH, months[m[2]]);
   c.set(DAYOFMONTH, ToInt(m[1]));
   c.set(HOUROFDAY, ToInt(m[4]));
   c.set(MINUTE, ToInt(m[5]));
   c.set(SECOND, ToInt(m[6]));
   return c;
end;

var GetMillis = fun(s)
   var c = GetTimeObj(s);
   return c.getTimeInMillis();
end;

var GetDay = fun(s)
   // Fri, 5 Jul 2019 10:31:35 +0000
   var m = Str_Match(s, `\w+, (\d+) (\w+) (\d+) (\d+)\:(\d+)\:(\d+) \+0000`);

   return ToString(m[2]) + ToString(m[1]) + "-" + ToString(m[3])
end;
  
var formatDate = fun(c)
   var YEAR = 1;
   var DAY = 5;
   var MONTH = 2;
   return ToString(c.get(MONTH)+1) + "-" + ToString(c.get(DAY)) + "-" + ToString(c.get(YEAR))
end;

var formatDuration = fun(minutes)
   var days = minutes div (60 * 24);
   var rest = minutes mod (60 * 24);
   var hours = rest div 60;
   return ToString(days) + " days " + ToString(hours) + " hours"
end;
  
var TestTitle = fun(title)
   var res = false;
   var tests = [ "danieltest", "proddtest", "deployment", "externalteam", "[CAP-1]", "[CAP-250]", "teamkelly", "vivqa" ];
   every t in tests do
      if Str_IndexOf(t, title) >= 0 then
         res = true
      end
   end;
     
   return res
end;

var ParseReviews = fun()
  
   if (url == "latest" or url == "test") then
      // url = "http://adam.cheyer.com/Bixby/SearchRequest-july.xml"
      url = "http://www.adam.cheyer.com/Bixby/SearchRequest-all.xml"
   end;
   var P = GetURL(url, nil, nil, [. mimetype="text/xml" .]) ? nil;

   var res = [];

   if (P != nil) then
      every item in Elem(P, "item") do
        var ok = true;
        var title = GetField(P, "title", item);
        if (title != nil) and !TestTitle(title) then
           var elt = [. .];
           var f = Str_Match(title, `\[CAP-(\d+)\] (\w+).(\w+)\@(\d+.\d+.\d+)`);
           if f == nil then
             elt.nomatch := title;
             ok = false
           else 
             elt.name :=f[2] + "." + f[3];
             elt.version := f[4]
           end;
           var c = GetMillis(GetField(P, "created", item));
           var u = GetMillis(GetField(P, "updated", item));
           elt.status := GetField(P, "status", item);
           elt.resolution := GetField(P, "resolution", item);
           elt.assignee := GetField(P, "assignee", item);
           elt.created := c div 1000 div 60;  // in minutes
           elt.updated := u div 1000 div 60;  // in minutes
           elt.dateCreated := GetDay(GetField(P, "created", item));
           elt.dateUpdated := GetDay(GetField(P, "updated", item));
		   if u > maxTime then
              maxTime = u;
              endTimeStr = GetField(P, "updated", item);
   			  endTimeC = GetTimeObj(endTimeStr);
              if minTime < 10 then
                 minTime = u;
              end;
           end;
           if c < minTime then
              minTime = c;
              startTimeStr = GetField(P, "created", item);
   			  startTimeC = GetTimeObj(startTimeStr);
           end;
           if ok then
              res = res + [ elt ]
           end
        // else 
           //res = res + [ "Error: " + title ]
        end
      end;  // every
   end;
   return res
end;

var GetStatus = fun(info, reviews)
   var completed = 0;
   var inProgress = 0;
   var requested = 0;
   var manager = 0;
   var err = "";
   every r in reviews do
     if r.status == "Review Completed" then
        completed = completed + 1
     elsif r.status == "Review In Progress" then
        inProgress = inProgress + 1
     elsif r.status == "Review Requested" then
        requested = requested + 1
     elsif r.status == "Manager Review" then
        manager = manager + 1
     else
        err = err + "|status:" + r.status
     end
   end;
   info.total := Size(reviews);
   info.completed := completed;
   info.inProgress := inProgress;
   info.requested := requested;
   info.manager := manager;
   // info.err := err;
end;
  
var GetTimes = fun(info, reviews, weekly)
   var t;
   var longestCompleted = 0;
   var timeCompleted = 0;
   var numCompleted = 0;
   var issueCompleted;

   var longestInProgress = 0;
   var timeInProgress = 0;
   var numInProgress = 0;
   var issueInProgress;

   var longestWaiting = 0;
   var timeWaiting = 0;
   var numWaiting = 0;
   var issueWaiting;

   var longestManager = 0;
   var timeManager = 0;
   var numManager = 0;
   var issueManager;
   
   var timeRejected = 0;
   var numRejected = 0;
   var longestRejected = 0;
   var issueRejected;
   var timeApproved = 0;
   var numApproved = 0;
   var longestApproved = 0;
   var issueApproved;
   
   var c = 0;
   var createdPerDay = [. .];
   var resolvedPerDay = [. .];
   
   every r in reviews do
   
      var c = createdPerDay[r.dateCreated] ? 0;
      c = c + 1;
      createdPerDay[r.dateCreated] := c;
   
     if r.status == "Review Completed" then
        var c = resolvedPerDay[r.dateCreated] ? 0;
        c = c + 1;
        resolvedPerDay[r.dateCreated] := c;
   
        t = r.updated - r.created;
        if t > longestCompleted then
           longestCompleted = t;
		   issueCompleted = r.name + " v" + r.version ? r.nomatch
        end;
        timeCompleted = timeCompleted + t;
        numCompleted = numCompleted + 1;
   
        if r.resolution == "Rejected" then
           timeRejected = timeRejected + t;
           numRejected = numRejected + 1;
           if t > longestRejected then
              longestRejected = t;
		      issueRejected = r.name + " v" + r.version ? r.nomatch
           end;
        else
           timeApproved = timeApproved + t;
           numApproved = numApproved + 1;
           if t > longestApproved then
              longestApproved = t;
		      issueApproved = r.name + " v" + r.version ? r.nomatch
           end;
        end
   
     elsif r.status == "Review In Progress" then
        t = r.updated - r.created;
        if t > longestInProgress then
           longestInProgress = t;
		   issueInProgress = r.name + " v" + r.version ? r.nomatch
        end;
        timeInProgress = timeInProgress + t;
        numInProgress = numInProgress + 1
     elsif r.status == "Review Requested" then
        t = r.updated - r.created;
        if t > longestWaiting then
           longestWaiting = t;
		   issueWaiting = r.name + " v" + r.version ? r.nomatch
        end;
        timeWaiting = timeWaiting + t;
        numWaiting = numWaiting + 1
     elsif r.status == "Manager Review" then
        t = r.updated - r.created;
        if t > longestManager then
           longestManager = t;
		   issueManager = r.name + " v" + r.version ? r.nomatch
        end;
        timeManager = timeManager + t;
        numManager = numManager + 1
     end
   end;
  
   if weekly then 
      if (numCompleted > 0) then
         info.averageCompletedHours := (timeCompleted div numCompleted) div 60;  // in hours
      else
         info.averageCompletedHours := 0;
      end;
      if (numRejected > 0) then
         info.averageRejectedHours := (timeRejected div numRejected) div 60;
      else
         info.averageRejectedHours := 0;
      end; 
      if (numApproved > 0) then
         info.averageApprovedHours := (timeApproved div numApproved) div 60;
      else
         info.averageApprovedHours := 0;
      end; 
   
   else
      info.longestCompleted := formatDuration(longestCompleted);
      info.longestCompletedIssue := issueCompleted;
      if (numCompleted > 0) then
         info.averageCompleted := formatDuration(timeCompleted div numCompleted);
      else
         info.averageCompleted := -1;
      end;
  
      info.longestRejected := formatDuration(longestRejected);
      info.longestRejectedIssue := issueRejected;
      if (numRejected > 0) then
         info.averageRejected := formatDuration(timeRejected div numRejected);
      else
         info.averageRejected := -1;
      end; 
  
      info.longestApproved := formatDuration(longestApproved);
      info.longestApprovedIssue := issueApproved;
      if (numApproved > 0) then
         info.averageApproved := formatDuration(timeApproved div numApproved);
      else
         info.averageApproved := -1;
      end; 

      info.longestInProgress := formatDuration(longestInProgress);
      info.longestInProgressIssue := issueInProgress;
      if (numInProgress > 0) then
         info.averageInProgress := formatDuration(timeInProgress div numInProgress);
      else
         info.averageInProgress := -1;
      end;

      info.longestWaiting := formatDuration(longestWaiting);
      info.longestWaitingIssue := issueWaiting;
      if (numWaiting > 0) then
         info.averageWaiting := formatDuration(timeWaiting div numWaiting);
      else
         info.averageWaiting := -1;
      end;

      info.longestManager := formatDuration(longestManager);
      info.longestManagerIssue := issueManager;
      if (numManager > 0) then   
         info.averageManager := formatDuration(timeManager div numManager);
      else
         info.averageManager := -1;
      end;
   end;
   
   c = 0;
   every cpd in createdPerDay do
      c = c + createdPerDay[cpd]
   end;   
   if (Size(ToList(createdPerDay)) > 0) then
      info.averageCreatedPerDay := (c * 100 div Size(ToList(createdPerDay))) / 100;
   else
      info.averageCreatedPerDay := -1
   end;
   info.totalCreatedIssues := c;

   c = 0;
   every rpd in resolvedPerDay do
      c = c + resolvedPerDay[rpd]
   end;
   if (Size(ToList(resolvedPerDay)) > 0) then
      info.averageResolvedPerDay := (c * 100 div Size(ToList(resolvedPerDay))) / 100;
   else
      info.averageResolvedPerDay := -1
   end;
   info.totalResolvedIssues := c;
   
   info.numReviews := Size(reviews)
end;
  
 
var GetByName = fun(info, reviews)
   var byName = [. .];
   var newByStatus = [. .];
   var newCapsules = 0;
           
   every r in reviews do
     var name = (r.name ? "null");   
   
     if (name != "null") then
        var s = (newByStatus[r.status] ? {});
        var s2 = s;
        s = s + { r.name };
        newByStatus[r.status] := s;
        if (Size(s) > Size(s2)) then
           newCapsules = newCapsules + 1
        end;
        if (r.status == "Review Completed") then
           var issues = (byName[name] ? []); 
           issues = [ r ] + issues;
           byName[name] := issues
        end
     end
   end;
   newByStatus["total"] := newCapsules;
   return [. byName = byName, newByStatus = newByStatus .]
end;
   
          
var GetRejections = fun(info, reviews)
   var bn = GetByName(info, reviews);
   var byName = bn.byName;
   var newByStatus = bn.newByStatus;
   var totApproved = 0;
   var totApprovedVersions = 0;
   var totRejectedVersions = 0;
   var rejectedUntilApproved = 0;
   var longestTurns = -1;
   var longest;
   every name in byName do
      var rej = 0;
      var approved = false;
      every r in byName[name] do
           if r.resolution == "Rejected" then
              rej = rej + 1;
              totRejectedVersions = totRejectedVersions + 1;
           elsif r.resolution == "Approved" then
              if rej > longestTurns then
                 longestTurns = rej;
                 longest = byName[name];               
              end;
              rejectedUntilApproved = rejectedUntilApproved + rej;
              totApprovedVersions = totApprovedVersions + 1;
              approved = true;
              rej = 0
           end;
      end;
      if approved then
         totApproved = totApproved + 1
      end
   end;
   
   info.newCapsules := newByStatus["total"] ? 0;
   info.newRequested := newByStatus["Review Requested"] ? {};
   info.newInProgress := newByStatus["Review In Progress"] ? {};
   info.newManager := newByStatus["Manager Review"] ? {};
   info.newCompleted := newByStatus["Review Completed"] ? {};
   
   info.averageTurnsToApproval := ((rejectedUntilApproved + totApprovedVersions) * 100 div totApprovedVersions) / 100;
   info.totRejectedVersions := totRejectedVersions;
   info.rejectedUntilApproved := rejectedUntilApproved;
   info.totApprovedVersions := totApprovedVersions;
   info.totApproved := totApproved;
   info.longestTurnsToApproval := longest;
   info.longestTurns := longestTurns + 1;
   // info.byName := byName
end;
           
var GetWeeklyStats = fun(info, reviews)
   var DAYOFWEEK = 7;
   var WEEKOFYEAR = 3;
   
   var startPeriod = startTimeC.clone();
   startPeriod.set(DAYOFWEEK, 1);
   var weekly = [. .];
   
   while startPeriod.getTimeInMillis() < endTimeC.getTimeInMillis() do
      var info2 = [. .];
      var endPeriod = startPeriod.clone();
      endPeriod.set(WEEKOFYEAR, endPeriod.get(WEEKOFYEAR)+1);
   
      var slice = GetTimeSlice(reviews, startPeriod.getTimeInMillis() div 1000 div 60, endPeriod.getTimeInMillis() div 1000 div 60);

      GetStatus(info2, slice);
      GetTimes(info2, slice, true);
      // GetRejections(info2, slice); 

      weekly[formatDate(startPeriod)] := info2;
      startPeriod = endPeriod.clone();
   end;
   info.weekly := weekly
end;   
   
   
var reviews = ParseReviews();     
var teams = SplitByTeam(reviews);

var res = [. startTime = startTimeStr, endTime = endTimeStr .];       

every team in teams do           
   var info = [. .];
   GetStatus(info, teams[team]);
   GetTimes(info, teams[team], false);
   GetRejections(info, teams[team]); 
   GetWeeklyStats(info, teams[team]);
   res[team] := info;     
end;
res;