From c5158a6457bbe0205201cbfae611e2559d68b15b Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 22 Nov 2010 23:54:16 -0500 Subject: [PATCH] Burndown Chart - Now renders (and wraps if necessary) a chart title, with additional filter description. - Removed more redundant code. - Fixed divide-by-zero bug when the filter resulted in zero matches. - Made the burndown commands read-only, so that filters apply. --- src/Cmd.cpp | 3 ++ src/burndown.cpp | 134 +++++++++++++++++++++++++++++------------------ 2 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/Cmd.cpp b/src/Cmd.cpp index 9acd1ee35..288249084 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -247,6 +247,9 @@ bool Cmd::isReadOnlyCommand () command == "history.annual" || command == "ghistory.monthly" || command == "ghistory.annual" || + command == "burndown.daily" || + command == "burndown.weekly" || + command == "burndown.monthly" || command == context.stringtable.get (CMD_CALENDAR, "calendar") || command == context.stringtable.get (CMD_COLORS, "colors") || command == context.stringtable.get (CMD_DIAGNOSTICS, "diagnostics") || diff --git a/src/burndown.cpp b/src/burndown.cpp index a6bf66d09..cdc7365f8 100644 --- a/src/burndown.cpp +++ b/src/burndown.cpp @@ -138,10 +138,11 @@ class Chart { public: Chart (char); - Chart (const Chart&); - Chart& operator= (const Chart&); + Chart (const Chart&); // Unimplemented + Chart& operator= (const Chart&); // Unimplemented ~Chart (); + void description (const std::string&); void scan (std::vector &); std::string render (); @@ -156,24 +157,25 @@ private: void yLabels (std::vector &); public: - int width; - int height; - int graph_width; - int graph_height; - int max_value; - int max_label; - std::vector labels; - int estimated_bars; - int actual_bars; - std::map bars; - Date earliest; - int carryover_done; - char period; // D, W, M. - std::string grid; + int width; // Terminal width + int height; // Terminal height + int graph_width; // Width of plot area + int graph_height; // Height of plot area + int max_value; // Largest combined bar value + int max_label; // Longest y-axis label + std::vector labels; // Y-axis labels + int estimated_bars; // Estimated bar count + int actual_bars; // Calculated bar count + std::map bars; // Epoch-indexed set of bars + Date earliest; // Date of earliest estimated bar + int carryover_done; // Number of 'done' tasks prior to chart range + char period; // D, W, M + std::string title; // Additional description + std::string grid; // String representing grid of characters - float find_rate; - float fix_rate; - std::string completion; + float find_rate; // Calculated find rate + float fix_rate; // Calculated fix rate + std::string completion; // Estimated completion date }; //////////////////////////////////////////////////////////////////////////////// @@ -215,44 +217,17 @@ Chart::Chart (char type) fix_rate = 0.0; } -//////////////////////////////////////////////////////////////////////////////// -Chart::Chart (const Chart& other) -{ - *this = other; -} - -//////////////////////////////////////////////////////////////////////////////// -Chart& Chart::operator= (const Chart& other) -{ - if (this != &other) - { - width = other.width; - height = other.height; - graph_width = other.graph_width; - graph_height = other.graph_height; - max_value = other.max_value; - max_label = other.max_label; - labels = other.labels; - estimated_bars = other.estimated_bars; - actual_bars = other.actual_bars; - bars = other.bars; - earliest = other.earliest; - carryover_done = other.carryover_done; - period = other.period; - grid = other.grid; - find_rate = other.find_rate; - fix_rate = other.fix_rate; - completion = other.completion; - } - - return *this; -} - //////////////////////////////////////////////////////////////////////////////// Chart::~Chart () { } +//////////////////////////////////////////////////////////////////////////////// +void Chart::description (const std::string& value) +{ + title = value; +} + //////////////////////////////////////////////////////////////////////////////// void Chart::scan (std::vector & tasks) { @@ -442,12 +417,44 @@ std::string Chart::render () return "Terminal window too small to draw a graph.\n"; } + if (max_value == 0) + return "No matches.\n"; + // Create a grid, folded into a string. // TODO Upgrade grid to a vector of strings, for simpler optimization. grid = ""; for (int i = 0; i < height; ++i) grid += std::string (width, ' ') + "\n"; + // Title. + std::string full_title; + switch (period) + { + case 'D': full_title = "Daily"; break; + case 'W': full_title = "Weekly"; break; + case 'M': full_title = "Monthly"; break; + } + + full_title += " Burndown"; + + if (title.length ()) + { + if (full_title.length () + 1 + title.length () < (unsigned) width) + { + full_title += " " + title; + grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); + } + else + { + grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); + grid.replace (LOC (1, (width - title.length ()) / 2), title.length (), title); + } + } + else + { + grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); + } + // Legend. grid.replace (LOC (graph_height / 2 - 1, width - 10), 10, "dd Done "); grid.replace (LOC (graph_height / 2, width - 10), 10, "ss Started"); @@ -980,6 +987,29 @@ int handleReportBurndownMonthly (std::string& outs) // Create a chart, scan the tasks, then render. Chart chart ('M'); + + // Use any filter as a title. + if (context.filter.size ()) + { + std::string combined = "("; + + for (unsigned int i = 0; i < context.filter.size (); ++i) + { + if (i) + combined += " "; + + combined += context.filter[i].name (); + + if (context.filter[i].mod ().length ()) + combined += "." + context.filter[i].mod (); + + combined += ":" + context.filter[i].value (); + } + + combined += ")"; + chart.description (combined); + } + chart.scan (tasks); std::map ::iterator it; for (it = chart.bars.begin (); it != chart.bars.end (); ++it)