From 7df94ddee8feb9bc19b681f2b58fc547df622c9b Mon Sep 17 00:00:00 2001 From: anuko Date: Tue, 26 Dec 2017 19:22:56 +0000 Subject: [PATCH] Initial implementation of a simple week view. --- WEB-INF/lib/ttTimeHelper.class.php | 109 +++++++++++++++++++++--- WEB-INF/templates/footer.tpl | 2 +- WEB-INF/templates/time.tpl | 2 - WEB-INF/templates/week.tpl | 78 +---------------- table_test.php | 2 +- week.php | 132 +++++++++++++++-------------- 6 files changed, 168 insertions(+), 157 deletions(-) diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index d186696d..ff3399ca 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -766,31 +766,57 @@ class ttTimeHelper { return $groupedRecords; } - /* This is work in progress, not working properly. - static function getDurationsForWeek($user_id, $start_date, $end_date) { + // getDataForWeekView - builds an array to render a table of durations for week view. + static function getDataForWeekView($user_id, $start_date, $end_date) { // Start by obtaining all records in interval. - // Then, iterate through them to build an array. $records = ttTimeHelper::getRecordsForInterval($user_id, $start_date, $end_date); - $durations_with_labels = array(); + $dataArray = array(); + + // Iterate through records and build $dataArray cell by cell. foreach ($records as $record) { + // Create record id without suffix. $record_id_no_suffix = ttTimeHelper::makeRecordIdentifier($record); // Handle potential multiple records with the same attributes by using a numerical suffix. $suffix = 0; $record_id = $record_id_no_suffix.'_'.$suffix; - while (!empty($durations_with_labels[$record_id][$record['date']])) { + $day_header = substr($record['date'], 8); // Day number in month. + while (ttTimeHelper::cellExists($record_id, $day_header, $dataArray)) { $suffix++; $record_id = $record_id_no_suffix.'_'.$suffix; } - $groupedRecords[$record_identifier][$record['date']] = array('id'=>$record['id'], 'duration'=>$record['duration']); - $groupedRecords[$record_identifier]['client'] = $record['client']; - $groupedRecords[$record_identifier]['cf_1_value'] = $record['cf_1_value']; - $groupedRecords[$record_identifier]['project'] = $record['project']; - $groupedRecords[$record_identifier]['task'] = $record['task']; - $groupedRecords[$record_identifier]['billable'] = $record['billable']; + // Find row. + $pos = ttTimeHelper::findRow($record_id, $dataArray); + if ($pos < 0) { + $dataArray[] = array('id' => $record_id,'label' => ttTimeHelper::makeRecordLabel($record)); // Insert row. + $pos = ttTimeHelper::findRow($record_id, $dataArray); + } + // Insert cell data from $record. + $dataArray[$pos][$day_header] = array('id' => $record['id'],'duration' => $record['duration']); } + return $dataArray; + } + + // cellExists is a helper function for getDataForWeekView() to see if a cell with a given label + // and a day header already exists. + static function cellExists($record_id, $day_header, $dataArray) { + foreach($dataArray as $row) { + if ($row['id'] == $record_id && !empty($row[$day_header]['duration'])) + return true; + } + return false; + } + + // findRow returns an existing row position in $dataArray, -1 otherwise. + static function findRow($record_id, $dataArray) { + $pos = 0; // Row position in array. + foreach($dataArray as $row) { + if ($row['id'] == $record_id) + return $pos; + $pos++; // Increment for search. + } + return -1; // Row not found. } - */ // makeRecordIdentifier - builds a string identifying a record for a grouped display (such as a week view). // For example: @@ -820,6 +846,39 @@ class ttTimeHelper { return $record_identifier; } + // makeRecordLabel - builds a human readable label for a row in week view, + // which is a combination ot record properties. + // Client - Project - Task - Custom field 1. + // Note that billable property is not part of the label. Instead, we intend to + // identify such records with a different color in week view. + static function makeRecordLabel($record) { + // TODO: debug this function. + global $user; + // Start with client. + if ($user->isPluginEnabled('cl')) + $label = $record['client']; + + // Add project. + $project = $record['project'] ? $record['project'] : ''; + if (!empty($label)) $label .= ' - '; + $label .= $project; + + // Add task. + $task = $record['task'] ? $record['task'] : ''; + if (!empty($label)) $label .= ' - '; + $label .= $task; + + // Add custom field 1. + if ($user->isPluginEnabled('cf')) { + if ($record['cf_1_value']) { + if (!empty($label)) $label .= ' - '; + $label .= $record['cf_1_value']; + } + } + + return $label; + } + // getGroupedRecordsTotals - returns day totals for grouped records. static function getGroupedRecordsTotals($groupedRecords) { $groupedRecordsTotals = array(); @@ -843,7 +902,7 @@ class ttTimeHelper { static function getDayHeadersForWeek($start_date) { $dayHeaders = array(); $objDate = new DateAndTime(DB_DATEFORMAT, $start_date); - $dayHeaders['day_header_0'] = $objDate->getDate(); + $dayHeaders['day_header_0'] = (string)$objDate->getDate(); // It returns an int on first call. Why? $objDate->incDay(); $dayHeaders['day_header_1'] = $objDate->getDate(); $objDate->incDay(); @@ -859,4 +918,28 @@ class ttTimeHelper { unset($objDate); return $dayHeaders; } + + // getDayTotals calculates total durations for each day from the existing data in $dataArray. + static function getDayTotals($dataArray, $dayHeaders) { + $dayTotals = array(); + + // Insert label. + global $i18n; + $dayTotals['label'] = $i18n->getKey('label.total'); + + foreach ($dataArray as $row) { + foreach($dayHeaders as $dayHeader) { + if (array_key_exists($dayHeader, $row)) { + $minutes = ttTimeHelper::toMinutes($row[$dayHeader]['duration']); + $dayTotals[$dayHeader] += $minutes; + } + } + } + // Convert minutes to hh:mm for display. + foreach($dayHeaders as $dayHeader) { + $dayTotals[$dayHeader] = ttTimeHelper::toAbsDuration($dayTotals[$dayHeader]); + } + return $dayTotals; + } } + diff --git a/WEB-INF/templates/footer.tpl b/WEB-INF/templates/footer.tpl index d94d6624..aafa107b 100644 --- a/WEB-INF/templates/footer.tpl +++ b/WEB-INF/templates/footer.tpl @@ -12,7 +12,7 @@
-
 Anuko Time Tracker 1.12.3.3693 | Copyright © Anuko | +  Anuko Time Tracker 1.13.3.3694 | Copyright © Anuko | {$i18n.footer.credits} | {$i18n.footer.license} | {$i18n.footer.improve} diff --git a/WEB-INF/templates/time.tpl b/WEB-INF/templates/time.tpl index d5db36a0..182f0127 100644 --- a/WEB-INF/templates/time.tpl +++ b/WEB-INF/templates/time.tpl @@ -8,13 +8,11 @@ {$forms.timeRecordForm.open} -{if defined(WEEK_VIEW_DEBUG)} -{/if}
{$i18n.label.day_view} / {$i18n.label.week_view}
diff --git a/WEB-INF/templates/week.tpl b/WEB-INF/templates/week.tpl index 0d279ec6..4abfa379 100644 --- a/WEB-INF/templates/week.tpl +++ b/WEB-INF/templates/week.tpl @@ -38,87 +38,11 @@
{$forms.weekTimeForm.week_durations.control}
- - - - - -
-{if $grouped_records} - - - {if ($user->isPluginEnabled('cl') || ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode))} - - {/if} - {if ($user->isPluginEnabled('cf') || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} - - {/if} - - - - - - - - - {foreach $grouped_records as $record} - - {if ($user->isPluginEnabled('cl') || ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode))} - - {/if} - {if ($user->isPluginEnabled('cf') || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} - - {/if} - - - - - - - - - {/foreach} - - {if ($user->isPluginEnabled('cl') || ($smarty.const.MODE_PROJECTS == $user->tracking_mode || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode))} - - {/if} - {if ($user->isPluginEnabled('cf') || $smarty.const.MODE_PROJECTS_AND_TASKS == $user->tracking_mode)} - - {/if} - - - - - - - - -
{$day_header_0}{$day_header_1}{$day_header_2}{$day_header_3}{$day_header_4}{$day_header_5}{$day_header_6}
{$record.project|escape}

{$record.client|escape}

{$record.task|escape}

{$record.cf_1_value|escape}

{$record.$date_0.duration}{$record.$date_1.duration}{$record.$date_2.duration}{$record.$date_3.duration}{$record.$date_4.duration}{$record.$date_5.duration}{$record.$date_6.duration}
{$grouped_records_totals.$date_0}{$grouped_records_totals.$date_1}{$grouped_records_totals.$date_2}{$grouped_records_totals.$date_3}{$grouped_records_totals.$date_4}{$grouped_records_totals.$date_5}{$grouped_records_totals.$date_6}
-{/if} -
{$forms.weekTimeForm.btn_submit.control}
+--> {$forms.weekTimeForm.close} diff --git a/table_test.php b/table_test.php index d4b21c74..f8b4c725 100644 --- a/table_test.php +++ b/table_test.php @@ -73,7 +73,7 @@ class LabelCellRenderer extends DefaultCellRenderer { // Define rendering class for a single cell for time entry in week view table. class TimeCellRenderer extends DefaultCellRenderer { function render(&$table, $value, $row, $column, $selected = false) { - $field_name = $table->getValueAtName($row,$column)['id']; // Our text field names (and ids) are like x_y (row_column). + $field_name = $table->getValueAt($row,$column)['id']; // Our text field names (and ids) are like x_y (row_column). $field = new TextField($field_name); $field->setFormName($table->getFormName()); $field->setSize(2); diff --git a/week.php b/week.php index 9e1d5180..a3f30cbc 100644 --- a/week.php +++ b/week.php @@ -65,8 +65,64 @@ $endDate = new DateAndTime(); $endDate->setTimestamp(mktime(0,0,0,$t_arr[4]+1,$t_arr[3]-$t_arr[6]+6+$startWeekBias,$t_arr[5])); // The above is needed to set date range (timestring) in page title. +// Use custom fields plugin if it is enabled. +if ($user->isPluginEnabled('cf')) { + require_once('plugins/CustomFields.class.php'); + $custom_fields = new CustomFields($user->team_id); + $smarty->assign('custom_fields', $custom_fields); +} + +// TODO: how is this plugin supposed to work for week view? +if ($user->isPluginEnabled('mq')){ + require_once('plugins/MonthlyQuota.class.php'); + $quota = new MonthlyQuota(); + $month_quota = $quota->get($selected_date->mYear, $selected_date->mMonth); + $month_total = ttTimeHelper::getTimeForMonth($user->getActiveUser(), $selected_date); + $minutes_left = ttTimeHelper::toMinutes($month_quota) - ttTimeHelper::toMinutes($month_total); + + $smarty->assign('month_total', $month_total); + $smarty->assign('over_quota', $minutes_left < 0); + $smarty->assign('quota_remaining', ttTimeHelper::toAbsDuration($minutes_left)); +} + +// Initialize variables. +// Custom field. +$cl_cf_1 = trim($request->getParameter('cf_1', ($request->getMethod()=='POST'? null : @$_SESSION['cf_1']))); +$_SESSION['cf_1'] = $cl_cf_1; +$cl_billable = 1; +if ($user->isPluginEnabled('iv')) { + if ($request->isPost()) { + $cl_billable = $request->getParameter('billable'); + $_SESSION['billable'] = (int) $cl_billable; + } else + if (isset($_SESSION['billable'])) + $cl_billable = $_SESSION['billable']; +} +$on_behalf_id = $request->getParameter('onBehalfUser', (isset($_SESSION['behalf_id'])? $_SESSION['behalf_id'] : $user->id)); +$cl_client = $request->getParameter('client', ($request->getMethod()=='POST'? null : @$_SESSION['client'])); +$_SESSION['client'] = $cl_client; +$cl_project = $request->getParameter('project', ($request->getMethod()=='POST'? null : @$_SESSION['project'])); +$_SESSION['project'] = $cl_project; +$cl_task = $request->getParameter('task', ($request->getMethod()=='POST'? null : @$_SESSION['task'])); +$_SESSION['task'] = $cl_task; + + + + + + + + + + + + // Get column headers. $dayHeaders = ttTimeHelper::getDayHeadersForWeek($startDate->toString(DB_DATEFORMAT)); +// Build data array for the table. +$dataArray = ttTimeHelper::getDataForWeekView($user->getActiveUser(), $startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT)); +// Build day totals. +$dayTotals = ttTimeHelper::getDayTotals($dataArray, $dayHeaders); // TODO: replace these two sample arrays with real data. $durations_with_labels = array( @@ -120,67 +176,17 @@ class TimeCellRenderer extends DefaultCellRenderer { } } - - - - - //$durations = ttTimeHelper::getDurationsForWeek($user->getActiveUser(), $startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT)); -$groupedRecords = ttTimeHelper::getGroupedRecordsForInterval($user->getActiveUser(), $startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT)); - +//$groupedRecords = ttTimeHelper::getGroupedRecordsForInterval($user->getActiveUser(), $startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT)); +//$dayTotals = ttTimeHelper::getGroupedRecordsTotals($groupedRecords); -$dayTotals = ttTimeHelper::getGroupedRecordsTotals($groupedRecords); -// Use custom fields plugin if it is enabled. -if ($user->isPluginEnabled('cf')) { - require_once('plugins/CustomFields.class.php'); - $custom_fields = new CustomFields($user->team_id); - $smarty->assign('custom_fields', $custom_fields); -} - -// TODO: how is this plugin supposed to work for week view? -if ($user->isPluginEnabled('mq')){ - require_once('plugins/MonthlyQuota.class.php'); - $quota = new MonthlyQuota(); - $month_quota = $quota->get($selected_date->mYear, $selected_date->mMonth); - $month_total = ttTimeHelper::getTimeForMonth($user->getActiveUser(), $selected_date); - $minutes_left = ttTimeHelper::toMinutes($month_quota) - ttTimeHelper::toMinutes($month_total); - - $smarty->assign('month_total', $month_total); - $smarty->assign('over_quota', $minutes_left < 0); - $smarty->assign('quota_remaining', ttTimeHelper::toAbsDuration($minutes_left)); -} - -// Initialize variables. -$cl_start = trim($request->getParameter('start')); -$cl_finish = trim($request->getParameter('finish')); -$cl_duration = trim($request->getParameter('duration')); -$cl_note = trim($request->getParameter('note')); -// Custom field. -$cl_cf_1 = trim($request->getParameter('cf_1', ($request->getMethod()=='POST'? null : @$_SESSION['cf_1']))); -$_SESSION['cf_1'] = $cl_cf_1; -$cl_billable = 1; -if ($user->isPluginEnabled('iv')) { - if ($request->isPost()) { - $cl_billable = $request->getParameter('billable'); - $_SESSION['billable'] = (int) $cl_billable; - } else - if (isset($_SESSION['billable'])) - $cl_billable = $_SESSION['billable']; -} -$on_behalf_id = $request->getParameter('onBehalfUser', (isset($_SESSION['behalf_id'])? $_SESSION['behalf_id'] : $user->id)); -$cl_client = $request->getParameter('client', ($request->getMethod()=='POST'? null : @$_SESSION['client'])); -$_SESSION['client'] = $cl_client; -$cl_project = $request->getParameter('project', ($request->getMethod()=='POST'? null : @$_SESSION['project'])); -$_SESSION['project'] = $cl_project; -$cl_task = $request->getParameter('task', ($request->getMethod()=='POST'? null : @$_SESSION['task'])); -$_SESSION['task'] = $cl_task; // Elements of weekTimeForm. $form = new Form('weekTimeForm'); @@ -204,16 +210,16 @@ $table = new Table('week_durations'); // $table->setIAScript('markModified'); // TODO: write a script to mark table or particular cells as modified. $table->setTableOptions(array('width'=>'100%','cellspacing'=>'1','cellpadding'=>'3','border'=>'0')); $table->setRowOptions(array('valign'=>'top','class'=>'tableHeader')); -$table->setData($durations_with_labels); +$table->setData($dataArray); // $durations_with_labels); // Add columns to table. -$table->addColumn(new TableColumn('label', '', new LabelCellRenderer(), $totals['label'])); -$table->addColumn(new TableColumn('day_0', $dayHeaders['day_header_0'], new TimeCellRenderer(), $totals['day_0'])); -$table->addColumn(new TableColumn('day_1', $dayHeaders['day_header_1'], new TimeCellRenderer(), $totals['day_1'])); -$table->addColumn(new TableColumn('day_2', $dayHeaders['day_header_2'], new TimeCellRenderer(), $totals['day_2'])); -$table->addColumn(new TableColumn('day_3', $dayHeaders['day_header_3'], new TimeCellRenderer(), $totals['day_3'])); -$table->addColumn(new TableColumn('day_4', $dayHeaders['day_header_4'], new TimeCellRenderer(), $totals['day_4'])); -$table->addColumn(new TableColumn('day_5', $dayHeaders['day_header_5'], new TimeCellRenderer())); -$table->addColumn(new TableColumn('day_6', $dayHeaders['day_header_6'], new TimeCellRenderer())); +$table->addColumn(new TableColumn('label', '', new LabelCellRenderer(), $dayTotals['label'])); +$table->addColumn(new TableColumn($dayHeaders['day_header_0'], $dayHeaders['day_header_0'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_0']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_1'], $dayHeaders['day_header_1'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_1']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_2'], $dayHeaders['day_header_2'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_2']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_3'], $dayHeaders['day_header_3'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_3']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_4'], $dayHeaders['day_header_4'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_4']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_5'], $dayHeaders['day_header_5'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_5']])); +$table->addColumn(new TableColumn($dayHeaders['day_header_6'], $dayHeaders['day_header_6'], new TimeCellRenderer(), $dayTotals[$dayHeaders['day_header_6']])); $table->setInteractive(false); $form->addInputElement($table); @@ -464,8 +470,8 @@ $smarty->assign('selected_date', $selected_date); $smarty->assign('week_total', $week_total); $smarty->assign('day_total', ttTimeHelper::getTimeForDay($user->getActiveUser(), $cl_date)); //$groupedRecords = ttTimeHelper::getGroupedRecordsForInterval($user->getActiveUser(), $startDate->toString(DB_DATEFORMAT), $endDate->toString(DB_DATEFORMAT)); -$smarty->assign('grouped_records', $groupedRecords); -$smarty->assign('grouped_records_totals', ttTimeHelper::getGroupedRecordsTotals($groupedRecords)); +//$smarty->assign('grouped_records', $groupedRecords); +//$smarty->assign('grouped_records_totals', ttTimeHelper::getGroupedRecordsTotals($groupedRecords)); $smarty->assign('client_list', $client_list); $smarty->assign('project_list', $project_list); -- 2.20.1