From 08259d4f206438095308749b8cc2abbe629137da Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Mon, 6 Jul 2015 21:34:57 -0400 Subject: Add lead and cycle time for projects --- app/Console/ProjectDailyColumnStatsExport.php | 34 ++++ app/Console/ProjectDailyStatsCalculation.php | 28 ++++ app/Console/ProjectDailySummaryCalculation.php | 27 ---- app/Console/ProjectDailySummaryExport.php | 34 ---- app/Controller/Analytic.php | 40 ++++- app/Controller/Export.php | 2 +- app/Core/Base.php | 3 +- app/Model/ProjectAnalytic.php | 37 +++++ app/Model/ProjectDailyColumnStats.php | 193 +++++++++++++++++++++++ app/Model/ProjectDailyStats.php | 72 +++++++++ app/Model/ProjectDailySummary.php | 193 ----------------------- app/Schema/Mysql.php | 21 ++- app/Schema/Postgres.php | 20 ++- app/Schema/Sqlite.php | 20 ++- app/ServiceProvider/ClassProvider.php | 3 +- app/Subscriber/ProjectDailySummarySubscriber.php | 3 +- app/Template/analytic/lead_cycle_time.php | 42 +++++ app/Template/analytic/sidebar.php | 3 + 18 files changed, 512 insertions(+), 263 deletions(-) create mode 100644 app/Console/ProjectDailyColumnStatsExport.php create mode 100644 app/Console/ProjectDailyStatsCalculation.php delete mode 100644 app/Console/ProjectDailySummaryCalculation.php delete mode 100644 app/Console/ProjectDailySummaryExport.php create mode 100644 app/Model/ProjectDailyColumnStats.php create mode 100644 app/Model/ProjectDailyStats.php delete mode 100644 app/Model/ProjectDailySummary.php create mode 100644 app/Template/analytic/lead_cycle_time.php (limited to 'app') diff --git a/app/Console/ProjectDailyColumnStatsExport.php b/app/Console/ProjectDailyColumnStatsExport.php new file mode 100644 index 00000000..b9830662 --- /dev/null +++ b/app/Console/ProjectDailyColumnStatsExport.php @@ -0,0 +1,34 @@ +setName('export:daily-project-column-stats') + ->setDescription('Daily project column stats CSV export (number of tasks per column and per day)') + ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') + ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') + ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $data = $this->projectDailyColumnStats->getAggregatedMetrics( + $input->getArgument('project_id'), + $input->getArgument('start_date'), + $input->getArgument('end_date') + ); + + if (is_array($data)) { + Tool::csv($data); + } + } +} diff --git a/app/Console/ProjectDailyStatsCalculation.php b/app/Console/ProjectDailyStatsCalculation.php new file mode 100644 index 00000000..4b77c556 --- /dev/null +++ b/app/Console/ProjectDailyStatsCalculation.php @@ -0,0 +1,28 @@ +setName('projects:daily-stats') + ->setDescription('Calculate daily statistics for all projects'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $projects = $this->project->getAllByStatus(Project::ACTIVE); + + foreach ($projects as $project) { + $output->writeln('Run calculation for '.$project['name']); + $this->projectDailyColumnStats->updateTotals($project['id'], date('Y-m-d')); + $this->projectDailyStats->updateTotals($project['id'], date('Y-m-d')); + } + } +} diff --git a/app/Console/ProjectDailySummaryCalculation.php b/app/Console/ProjectDailySummaryCalculation.php deleted file mode 100644 index b2ada1b6..00000000 --- a/app/Console/ProjectDailySummaryCalculation.php +++ /dev/null @@ -1,27 +0,0 @@ -setName('projects:daily-summary') - ->setDescription('Calculate daily summary data for all projects'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $projects = $this->project->getAllByStatus(Project::ACTIVE); - - foreach ($projects as $project) { - $output->writeln('Run calculation for '.$project['name']); - $this->projectDailySummary->updateTotals($project['id'], date('Y-m-d')); - } - } -} diff --git a/app/Console/ProjectDailySummaryExport.php b/app/Console/ProjectDailySummaryExport.php deleted file mode 100644 index 07841d52..00000000 --- a/app/Console/ProjectDailySummaryExport.php +++ /dev/null @@ -1,34 +0,0 @@ -setName('export:daily-project-summary') - ->setDescription('Daily project summary CSV export (number of tasks per column and per day)') - ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') - ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') - ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $data = $this->projectDailySummary->getAggregatedMetrics( - $input->getArgument('project_id'), - $input->getArgument('start_date'), - $input->getArgument('end_date') - ); - - if (is_array($data)) { - Tool::csv($data); - } - } -} diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index 010fda09..ca2146ed 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -26,6 +26,40 @@ class Analytic extends Base return $this->template->layout('analytic/layout', $params); } + /** + * Show average Lead and Cycle time + * + * @access public + */ + public function leadAndCycleTime() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $this->projectDailyStats->updateTotals($project['id'], date('Y-m-d')); + + $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week'))); + $to = $this->request->getStringParam('to', date('Y-m-d')); + + if (! empty($values)) { + $from = $values['from']; + $to = $values['to']; + } + + $this->response->html($this->layout('analytic/lead_cycle_time', array( + 'values' => array( + 'from' => $from, + 'to' => $to, + ), + 'project' => $project, + 'average' => $this->projectAnalytic->getAverageLeadAndCycleTime($project['id']), + 'metrics' => $this->projectDailyStats->getRawMetrics($project['id'], $from, $to), + 'date_format' => $this->config->get('application_date_format'), + 'date_formats' => $this->dateParser->getAvailableFormats(), + 'title' => t('Lead and Cycle time for "%s"', $project['name']), + ))); + } + /** * Show average time spent by column * @@ -104,6 +138,8 @@ class Analytic extends Base $project = $this->getProject(); $values = $this->request->getValues(); + $this->projectDailyColumnStats->updateTotals($project['id'], date('Y-m-d')); + $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week'))); $to = $this->request->getStringParam('to', date('Y-m-d')); @@ -112,7 +148,7 @@ class Analytic extends Base $to = $values['to']; } - $display_graph = $this->projectDailySummary->countDays($project['id'], $from, $to) >= 2; + $display_graph = $this->projectDailyColumnStats->countDays($project['id'], $from, $to) >= 2; $this->response->html($this->layout($template, array( 'values' => array( @@ -120,7 +156,7 @@ class Analytic extends Base 'to' => $to, ), 'display_graph' => $display_graph, - 'metrics' => $display_graph ? $this->projectDailySummary->getAggregatedMetrics($project['id'], $from, $to, $column) : array(), + 'metrics' => $display_graph ? $this->projectDailyColumnStats->getAggregatedMetrics($project['id'], $from, $to, $column) : array(), 'project' => $project, 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), diff --git a/app/Controller/Export.php b/app/Controller/Export.php index 117fb5ee..8b558c0a 100644 --- a/app/Controller/Export.php +++ b/app/Controller/Export.php @@ -70,7 +70,7 @@ class Export extends Base */ public function summary() { - $this->common('projectDailySummary', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export')); + $this->common('ProjectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export')); } /** diff --git a/app/Core/Base.php b/app/Core/Base.php index d4d7faa3..14466d5c 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -48,7 +48,8 @@ use Pimple\Container; * @property \Model\ProjectActivity $projectActivity * @property \Model\ProjectAnalytic $projectAnalytic * @property \Model\ProjectDuplication $projectDuplication - * @property \Model\ProjectDailySummary $projectDailySummary + * @property \Model\ProjectDailyColumnStats $projectDailyColumnStats + * @property \Model\ProjectDailyStats $projectDailyStats * @property \Model\ProjectIntegration $projectIntegration * @property \Model\ProjectPermission $projectPermission * @property \Model\Subtask $subtask diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php index f4e8af09..1ee8a405 100644 --- a/app/Model/ProjectAnalytic.php +++ b/app/Model/ProjectAnalytic.php @@ -88,6 +88,43 @@ class ProjectAnalytic extends Base return array_values($metrics); } + /** + * Get the average lead and cycle time + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAverageLeadAndCycleTime($project_id) + { + $stats = array( + 'count' => 0, + 'total_lead_time' => 0, + 'total_cycle_time' => 0, + 'avg_lead_time' => 0, + 'avg_cycle_time' => 0, + ); + + $tasks = $this->db + ->table(Task::TABLE) + ->columns('date_completed', 'date_creation', 'date_started') + ->eq('project_id', $project_id) + ->desc('id') + ->limit(1000) + ->findAll(); + + foreach ($tasks as &$task) { + $stats['count']++; + $stats['total_lead_time'] += ($task['date_completed'] ?: time()) - $task['date_creation']; + $stats['total_cycle_time'] += empty($task['date_started']) ? 0 : ($task['date_completed'] ?: time()) - $task['date_started']; + } + + $stats['avg_lead_time'] = (int) ($stats['total_lead_time'] / $stats['count']); + $stats['avg_cycle_time'] = (int) ($stats['total_cycle_time'] / $stats['count']); + + return $stats; + } + /** * Get the average time spent into each column * diff --git a/app/Model/ProjectDailyColumnStats.php b/app/Model/ProjectDailyColumnStats.php new file mode 100644 index 00000000..26e9d8b7 --- /dev/null +++ b/app/Model/ProjectDailyColumnStats.php @@ -0,0 +1,193 @@ +db->transaction(function($db) use ($project_id, $date) { + + $column_ids = $db->table(Board::TABLE)->eq('project_id', $project_id)->findAllByColumn('id'); + + foreach ($column_ids as $column_id) { + + // This call will fail if the record already exists + // (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE) + $db->table(ProjectDailyColumnStats::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'column_id' => $column_id, + 'total' => 0, + 'score' => 0, + )); + + $db->table(ProjectDailyColumnStats::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('day', $date) + ->update(array( + 'score' => $db->table(Task::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('is_active', Task::STATUS_OPEN) + ->sum('score'), + 'total' => $db->table(Task::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->count() + )); + } + }); + } + + /** + * Count the number of recorded days for the data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return integer + */ + public function countDays($project_id, $from, $to) + { + $rq = $this->db->execute( + 'SELECT COUNT(DISTINCT day) FROM '.self::TABLE.' WHERE day >= ? AND day <= ? AND project_id=?', + array($from, $to, $project_id) + ); + + return $rq !== false ? $rq->fetchColumn(0) : 0; + } + + /** + * Get raw metrics for the project within a data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetrics($project_id, $from, $to) + { + return $this->db->table(ProjectDailyColumnStats::TABLE) + ->columns( + ProjectDailyColumnStats::TABLE.'.column_id', + ProjectDailyColumnStats::TABLE.'.day', + ProjectDailyColumnStats::TABLE.'.total', + ProjectDailyColumnStats::TABLE.'.score', + Board::TABLE.'.title AS column_title' + ) + ->join(Board::TABLE, 'id', 'column_id') + ->eq(ProjectDailyColumnStats::TABLE.'.project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(ProjectDailyColumnStats::TABLE.'.day') + ->findAll(); + } + + /** + * Get raw metrics for the project within a data range grouped by day + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetricsByDay($project_id, $from, $to) + { + return $this->db->table(ProjectDailyColumnStats::TABLE) + ->columns( + ProjectDailyColumnStats::TABLE.'.day', + 'SUM('.ProjectDailyColumnStats::TABLE.'.total) AS total', + 'SUM('.ProjectDailyColumnStats::TABLE.'.score) AS score' + ) + ->eq(ProjectDailyColumnStats::TABLE.'.project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(ProjectDailyColumnStats::TABLE.'.day') + ->groupBy(ProjectDailyColumnStats::TABLE.'.day') + ->findAll(); + } + + /** + * Get aggregated metrics for the project within a data range + * + * [ + * ['Date', 'Column1', 'Column2'], + * ['2014-11-16', 2, 5], + * ['2014-11-17', 20, 15], + * ] + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @param string $column Column to aggregate + * @return array + */ + public function getAggregatedMetrics($project_id, $from, $to, $column = 'total') + { + $columns = $this->board->getColumnsList($project_id); + $column_ids = array_keys($columns); + $metrics = array(array_merge(array(e('Date')), array_values($columns))); + $aggregates = array(); + + // Fetch metrics for the project + $records = $this->db->table(ProjectDailyColumnStats::TABLE) + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->findAll(); + + // Aggregate by day + foreach ($records as $record) { + + if (! isset($aggregates[$record['day']])) { + $aggregates[$record['day']] = array($record['day']); + } + + $aggregates[$record['day']][$record['column_id']] = $record[$column]; + } + + // Aggregate by row + foreach ($aggregates as $aggregate) { + + $row = array($aggregate[0]); + + foreach ($column_ids as $column_id) { + $row[] = (int) $aggregate[$column_id]; + } + + $metrics[] = $row; + } + + return $metrics; + } +} diff --git a/app/Model/ProjectDailyStats.php b/app/Model/ProjectDailyStats.php new file mode 100644 index 00000000..56a51730 --- /dev/null +++ b/app/Model/ProjectDailyStats.php @@ -0,0 +1,72 @@ +projectAnalytic->getAverageLeadAndCycleTime($project_id); + + return $this->db->transaction(function($db) use ($project_id, $date, $lead_cycle_time) { + + // This call will fail if the record already exists + // (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE) + $db->table(ProjectDailyStats::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'avg_lead_time' => 0, + 'avg_cycle_time' => 0, + )); + + $db->table(ProjectDailyStats::TABLE) + ->eq('project_id', $project_id) + ->eq('day', $date) + ->update(array( + 'avg_lead_time' => $lead_cycle_time['avg_lead_time'], + 'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'], + )); + }); + } + + /** + * Get raw metrics for the project within a data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetrics($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->columns('day', 'avg_lead_time', 'avg_cycle_time') + ->eq(self::TABLE.'.project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(self::TABLE.'.day') + ->findAll(); + } +} diff --git a/app/Model/ProjectDailySummary.php b/app/Model/ProjectDailySummary.php deleted file mode 100644 index 04dc5629..00000000 --- a/app/Model/ProjectDailySummary.php +++ /dev/null @@ -1,193 +0,0 @@ -db->transaction(function($db) use ($project_id, $date) { - - $column_ids = $db->table(Board::TABLE)->eq('project_id', $project_id)->findAllByColumn('id'); - - foreach ($column_ids as $column_id) { - - // This call will fail if the record already exists - // (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE) - $db->table(ProjectDailySummary::TABLE)->insert(array( - 'day' => $date, - 'project_id' => $project_id, - 'column_id' => $column_id, - 'total' => 0, - 'score' => 0, - )); - - $db->table(ProjectDailySummary::TABLE) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->eq('day', $date) - ->update(array( - 'score' => $db->table(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->eq('is_active', Task::STATUS_OPEN) - ->sum('score'), - 'total' => $db->table(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->count() - )); - } - }); - } - - /** - * Count the number of recorded days for the data range - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return integer - */ - public function countDays($project_id, $from, $to) - { - $rq = $this->db->execute( - 'SELECT COUNT(DISTINCT day) FROM '.self::TABLE.' WHERE day >= ? AND day <= ? AND project_id=?', - array($from, $to, $project_id) - ); - - return $rq !== false ? $rq->fetchColumn(0) : 0; - } - - /** - * Get raw metrics for the project within a data range - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return array - */ - public function getRawMetrics($project_id, $from, $to) - { - return $this->db->table(ProjectDailySummary::TABLE) - ->columns( - ProjectDailySummary::TABLE.'.column_id', - ProjectDailySummary::TABLE.'.day', - ProjectDailySummary::TABLE.'.total', - ProjectDailySummary::TABLE.'.score', - Board::TABLE.'.title AS column_title' - ) - ->join(Board::TABLE, 'id', 'column_id') - ->eq(ProjectDailySummary::TABLE.'.project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->asc(ProjectDailySummary::TABLE.'.day') - ->findAll(); - } - - /** - * Get raw metrics for the project within a data range grouped by day - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return array - */ - public function getRawMetricsByDay($project_id, $from, $to) - { - return $this->db->table(ProjectDailySummary::TABLE) - ->columns( - ProjectDailySummary::TABLE.'.day', - 'SUM('.ProjectDailySummary::TABLE.'.total) AS total', - 'SUM('.ProjectDailySummary::TABLE.'.score) AS score' - ) - ->eq(ProjectDailySummary::TABLE.'.project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->asc(ProjectDailySummary::TABLE.'.day') - ->groupBy(ProjectDailySummary::TABLE.'.day') - ->findAll(); - } - - /** - * Get aggregated metrics for the project within a data range - * - * [ - * ['Date', 'Column1', 'Column2'], - * ['2014-11-16', 2, 5], - * ['2014-11-17', 20, 15], - * ] - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @param string $column Column to aggregate - * @return array - */ - public function getAggregatedMetrics($project_id, $from, $to, $column = 'total') - { - $columns = $this->board->getColumnsList($project_id); - $column_ids = array_keys($columns); - $metrics = array(array_merge(array(e('Date')), array_values($columns))); - $aggregates = array(); - - // Fetch metrics for the project - $records = $this->db->table(ProjectDailySummary::TABLE) - ->eq('project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->findAll(); - - // Aggregate by day - foreach ($records as $record) { - - if (! isset($aggregates[$record['day']])) { - $aggregates[$record['day']] = array($record['day']); - } - - $aggregates[$record['day']][$record['column_id']] = $record[$column]; - } - - // Aggregate by row - foreach ($aggregates as $aggregate) { - - $row = array($aggregate[0]); - - foreach ($column_ids as $column_id) { - $row[] = (int) $aggregate[$column_id]; - } - - $metrics[] = $row; - } - - return $metrics; - } -} diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 37ef637b..769a7425 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,26 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 78; +const VERSION = 79; + +function version_79($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INT NOT NULL AUTO_INCREMENT, + day CHAR(10) NOT NULL, + project_id INT NOT NULL, + avg_lead_time INT NOT NULL DEFAULT 0, + avg_cycle_time INT NOT NULL DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('RENAME TABLE project_daily_summaries TO project_daily_column_stats'); +} function version_78($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 62f22baa..fca87766 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,25 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 58; +const VERSION = 59; + +function version_59($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id SERIAL PRIMARY KEY, + day CHAR(10) NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} function version_58($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 10899b54..9981e72f 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,25 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 74; +const VERSION = 75; + +function version_75($pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INTEGER PRIMARY KEY, + day TEXT NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} function version_74($pdo) { diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index 609f5824..8c55457d 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -33,7 +33,8 @@ class ClassProvider implements ServiceProviderInterface 'ProjectActivity', 'ProjectAnalytic', 'ProjectDuplication', - 'ProjectDailySummary', + 'ProjectDailyColumnStats', + 'ProjectDailyStats', 'ProjectIntegration', 'ProjectPermission', 'Subtask', diff --git a/app/Subscriber/ProjectDailySummarySubscriber.php b/app/Subscriber/ProjectDailySummarySubscriber.php index 9e4f15b0..db180dea 100644 --- a/app/Subscriber/ProjectDailySummarySubscriber.php +++ b/app/Subscriber/ProjectDailySummarySubscriber.php @@ -22,7 +22,8 @@ class ProjectDailySummarySubscriber extends \Core\Base implements EventSubscribe public function execute(TaskEvent $event) { if (isset($event['project_id'])) { - $this->projectDailySummary->updateTotals($event['project_id'], date('Y-m-d')); + $this->projectDailyColumnStats->updateTotals($event['project_id'], date('Y-m-d')); + $this->projectDailyStats->updateTotals($event['project_id'], date('Y-m-d')); } } } diff --git a/app/Template/analytic/lead_cycle_time.php b/app/Template/analytic/lead_cycle_time.php new file mode 100644 index 00000000..d96bdcb8 --- /dev/null +++ b/app/Template/analytic/lead_cycle_time.php @@ -0,0 +1,42 @@ + + +
+ +
+ + +

+ +
+ +
+ +
+ + form->csrf() ?> + +
+ form->label(t('Start Date'), 'from') ?> + form->text('from', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> +
+ +
+ form->label(t('End Date'), 'to') ?> + form->text('to', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?> +
+ +
+ +
+
+ +

+ +

+
+ diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php index 1ffce710..de03bdf8 100644 --- a/app/Template/analytic/sidebar.php +++ b/app/Template/analytic/sidebar.php @@ -16,5 +16,8 @@
  • url->link(t('Average time into each column'), 'analytic', 'averageTimeByColumn', array('project_id' => $project['id'])) ?>
  • +
  • + url->link(t('Lead and cycle time'), 'analytic', 'leadAndCycleTime', array('project_id' => $project['id'])) ?> +
  • \ No newline at end of file -- cgit v1.2.3