use of net.sourceforge.processdash.util.DataPair in project processdash by dtuma.
the class WorkflowPlanSummary method printDefectsByPhaseCharts.
private void printDefectsByPhaseCharts(Map<String, DataPair>[] defectsByPhase) throws IOException {
if (isExportingToExcel())
return;
// shuffle the items in the "injected" list so "Before Development" is
// at the end. This makes the phase colors consistent across pie charts.
Map<String, DataPair> injected = new LinkedHashMap(defectsByPhase[0]);
DataPair before = injected.remove(Defect.BEFORE_DEVELOPMENT);
DataPair total = injected.remove(TOTAL_KEY);
injected.put(Defect.BEFORE_DEVELOPMENT, before);
injected.put(TOTAL_KEY, total);
Map removed = defectsByPhase[1];
out.print("<p>\n");
writePhaseChart(false, "Defects_Injected", "Defects", 1, injected);
writePhaseChart(false, "Defects_Removed", "Defects", 1, removed);
out.print("</p>\n");
}
use of net.sourceforge.processdash.util.DataPair in project processdash by dtuma.
the class WorkflowPlanSummary method writeOverallMetrics.
private void writeOverallMetrics(Map<String, DataPair> sizes, Map<String, DataPair> timeInPhase, Map<String, String> phaseTypes) {
DataPair totalTime = timeInPhase.get(TOTAL_KEY);
out.print("<h2 style='margin-top:0px'>");
out.print(res("Overall_Metrics"));
out.print("</h2>\n<table>\n");
printTableHeader(null, false);
// print numbers for productivity
for (Entry<String, DataPair> e : sizes.entrySet()) {
String metric = e.getKey();
String label = resources.format("Productivity_Units_FMT", metric);
DataPair productivity = new DataPair(e.getValue()).multiply(60).divide(totalTime);
printTableRow(label, productivity, Format.Number);
}
// print total time
printTableRow(res("Total_Time"), totalTime, Format.Time);
// print time estimating error
DataPair timeEst = new DataPair();
timeEst.actual = (totalTime.actual - totalTime.plan) / totalTime.plan;
printTableRow(res("Time_Estimating_Error"), timeEst, Format.Percent, true, 0);
// print CPI
DataPair cpi = new DataPair();
cpi.actual = totalTime.plan / totalTime.actual;
printTableRow(res("CPI"), cpi, Format.Number, true, 0);
// calculate cost of quality
DataPair appraisalCOQ = new DataPair();
DataPair failureCOQ = new DataPair();
for (Entry<String, DataPair> e : timeInPhase.entrySet()) {
String phaseName = e.getKey();
String phaseType = phaseTypes.get(phaseName);
if ("Appraisal".equals(phaseType))
appraisalCOQ.add(e.getValue());
else if ("Failure".equals(phaseType))
failureCOQ.add(e.getValue());
}
appraisalCOQ.divide(totalTime);
failureCOQ.divide(totalTime);
DataPair totalCOQ = new DataPair(appraisalCOQ).add(failureCOQ);
DataPair afr = new DataPair(appraisalCOQ).divide(failureCOQ);
printTableRow(res("%_Appraisal_COQ"), appraisalCOQ, Format.Percent);
printTableRow(res("%_Failure_COQ"), failureCOQ, Format.Percent);
printTableRow(res("%_Total_COQ"), totalCOQ, Format.Percent);
printTableRow(res("Appraisal_to_Failure_Ratio"), afr, Format.Number);
out.print("</table>\n");
}
use of net.sourceforge.processdash.util.DataPair in project processdash by dtuma.
the class WorkflowPlanSummary method writeContents.
@Override
protected void writeContents() throws IOException {
ChartData chartData = AnalysisPage.getChartData((HttpServletRequest) env.get(HttpServletRequest.class), true);
WorkflowHistDataHelper hist = chartData.histData;
if (hist.getWorkflowName() == null)
throw new TinyCGIException(404, "The requested workflow was not found.");
String title = resources.getString("Workflow.Analysis.Title") + " - " + hist.getWorkflowName();
out.print("<html><head><title>");
out.print(esc(title));
out.print("</title>\n");
out.print(cssLinkHTML());
if (hist.isFiltering())
out.write("<link rel='stylesheet' type='text/css' href='filter-style.css'>\n");
out.print(HTMLUtils.scriptLinkHtml("/lib/overlib.js"));
out.print("<style>\n");
out.print(" .rowLabel { padding-right: 10px }\n");
out.print(" th.plan, th.act { width: 70px; }\n");
out.print(" td.plan, td.act { padding-right: 4px; border: 1px solid gray; text-align: right }\n");
out.print(" #filter.collapsed .filterItem { display: none }\n");
out.print(" #filter.expanded .filterLink { display: none }\n");
out.print(" #defects th.plan, #defects td.plan { display: none }\n");
out.print("</style>\n");
out.print("<script>\n");
out.print(" function showFilter() {\n");
out.print(" document.getElementById('filter').className = 'expanded';\n");
out.print(" }\n");
out.print("</script>\n");
out.print("</head>\n");
out.print("<body><h1>");
out.print(esc(title));
out.print("</h1>\n");
out.write("<table><tr>\n<td style='vertical-align:baseline'><h2>");
out.print(esc(res("Summary.Title")));
out.write(" </td>\n");
if (!isExporting())
writePageSubtitle(hist);
out.write("</tr></table>\n");
Map<String, DataPair> sizes = hist.getAddedAndModifiedSizes();
Map<String, DataPair> timeInPhase = hist.getTotalTimeInPhase();
Map<String, DataPair>[] defectsByPhase = hist.getDefectsByPhase();
for (Iterator<String> i = sizes.keySet().iterator(); i.hasNext(); ) {
if (AnalysisPage.isTimeUnits(i.next()))
i.remove();
}
writeOverallMetrics(sizes, timeInPhase, hist.getPhaseTypes());
printTable("Size", "Added_&_Modified", sizes, Format.Number, false);
printTable("Time_in_Phase", null, timeInPhase, Format.Time, true);
printTimeInPhaseCharts(timeInPhase);
if (defectsByPhase[1].get(TOTAL_KEY).actual > 0) {
out.print("<div id=\"defects\">\n");
setBeforeAndAfterRowLabels(timeInPhase);
printTable("Defects_Injected", null, defectsByPhase[0], Format.Number, true);
printTable("Defects_Removed", null, defectsByPhase[1], Format.Number, true);
printDefectsByPhaseCharts(defectsByPhase);
writeAdvancedDefectMetrics(hist, defectsByPhase, timeInPhase);
out.print("</div>\n");
}
if (!isExportingToExcel()) {
out.print("<hr>\n");
out.print("<a href=\"excel.iqy?fullPage\">");
out.print(resources.getHTML("Export_to_Excel"));
out.print("</a>");
}
out.print("</body></html>\n");
if (parameters.containsKey("debug"))
hist.debugPrintEnactments();
}
use of net.sourceforge.processdash.util.DataPair in project processdash by dtuma.
the class WorkflowPlanSummary method applyLegacyMultiplier.
/**
* Teams that have been using the dashboard for a while will have legacy
* (MCF-phase) defects in their defect log. Once they begin collecting
* defect data against workflow phases, they will receive useful numbers for
* inj/rem % by phase, as well as yield. But unfortunately, the injection
* and removal rates will be too low, since we are dividing by time in phase
* (which includes time from legacy project cycles where the defects were
* collected the old way).
*
* To avoid this problem, we count the number of legacy defects and apply a
* scaling factor to let defect counts and time in phase relate in an
* apples-to-apples way. This is an engineering compromise, based on the
* observation that useful, meaningful defects rates are better than no
* rates at all.
*
* In the future, when teams collect all data against workflow phases, this
* method will be a no-op.
*/
private void applyLegacyMultiplier(Map<String, DataPair>[] defectsByPhase, Map<String, DataPair>... dataToFix) {
DataPair total = defectsByPhase[1].get(TOTAL_KEY);
DataPair unrecognized = defectsByPhase[2].get(TOTAL_KEY);
if (total.actual > 0 && unrecognized.actual > 0) {
double factor = 1 + (unrecognized.actual / total.actual);
for (Map<String, DataPair> oneDataSet : dataToFix) {
for (DataPair pair : oneDataSet.values()) pair.actual *= factor;
}
}
}
use of net.sourceforge.processdash.util.DataPair in project processdash by dtuma.
the class WorkflowPlanSummary method writeAdvancedDefectMetrics.
private void writeAdvancedDefectMetrics(WorkflowHistDataHelper hist, Map<String, DataPair>[] defectsByPhase, Map<String, DataPair> timeInPhase) {
// get the yields for the process
Map<String, DataPair>[] yields = hist.getYields();
Map<String, DataPair> processYields = yields[0];
Map<String, DataPair> phaseYields = yields[1];
// change the display name for the "total" row
DataPair totalProcessYield = processYields.remove(TOTAL_KEY);
processYields.put(res("Workflow.Analysis.Workflow_Completion"), totalProcessYield);
// to clean up the report, replace 0/0 yields with #DIV/0!
replaceNaNs(Double.POSITIVE_INFINITY, processYields, phaseYields);
printTable("Workflow.Analysis.Phase_Yields", null, phaseYields, Format.Percent, false);
printTable("Workflow.Analysis.Process_Yields", "Workflow.Analysis.%_Removed_Before", processYields, Format.Percent, false);
Map<String, DataPair> injRates = divide(defectsByPhase[0], timeInPhase);
Map<String, DataPair> remRates = divide(defectsByPhase[1], timeInPhase);
// convert minutes to hours
multiply(60, injRates, remRates);
// clean up 0/0 rates
replaceNaNs(0.0, injRates, remRates);
applyLegacyMultiplier(defectsByPhase, injRates, remRates);
printTable("Workflow.Analysis.Defect_Injection_Rates", "Defects_Injected_per_Hour", injRates, Format.Number, false);
printTable("Workflow.Analysis.Defect_Removal_Rates", "Defects_Removed_per_Hour", remRates, Format.Number, false);
}
Aggregations