New Column - recur

- Added new column 'recur' for use in custom reports.
- Implemented Table::ascendingPeriod, Table::descendingPeriod allowing
  sorting on the recur column.
- Added unit tests to both use the new column and test the sorting.
- Code cleanup.
This commit is contained in:
Paul Beckingham 2009-03-09 22:01:08 -04:00
parent 751094cffb
commit 17de9fec9f
9 changed files with 125 additions and 12 deletions

View file

@ -941,6 +941,24 @@ void Table::sort (std::vector <int>& order)
((std::string)*left == "M" && (std::string)*right == "H"))
SWAP
break;
case ascendingPeriod:
if ((std::string)*left == "" && (std::string)*right != "")
break;
else if ((std::string)*left != "" && (std::string)*right == "")
SWAP
else if (convertDuration ((std::string)*left) > convertDuration ((std::string)*right))
SWAP
break;
case descendingPeriod:
if ((std::string)*left != "" && (std::string)*right == "")
break;
else if ((std::string)*left == "" && (std::string)*right != "")
SWAP
else if (convertDuration ((std::string)*left) < convertDuration ((std::string)*right))
SWAP
break;
}
}
}

View file

@ -37,9 +37,16 @@ class Table
{
public:
enum just {left, center, right};
enum order {ascendingNumeric, ascendingCharacter, ascendingPriority,
ascendingDate, descendingNumeric, descendingCharacter,
descendingPriority, descendingDate};
enum order {ascendingNumeric,
ascendingCharacter,
ascendingPriority,
ascendingDate,
ascendingPeriod,
descendingNumeric,
descendingCharacter,
descendingPriority,
descendingDate,
descendingPeriod};
enum sizing {minimum = -1, flexible = 0};
Table ();

View file

@ -2447,6 +2447,16 @@ std::string handleCustomReport (
table.addCell (row, columnCount, tasks[row].getDescription ());
}
else if (*col == "recur")
{
table.addColumn ("Recur");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size (); ++row)
table.addCell (row, columnCount, tasks[row].getAttribute ("recur"));
}
// Common to all columns.
// Add underline.
if (conf.get (std::string ("color"), true) || conf.get (std::string ("_forcecolor"), false))
@ -2487,6 +2497,12 @@ std::string handleCustomReport (
Table::ascendingDate :
Table::descendingDate));
else if (column == "recur")
table.sortOn (columnIndex[column],
(direction == '+' ?
Table::ascendingPeriod :
Table::descendingPeriod));
else
table.sortOn (columnIndex[column],
(direction == '+' ?
@ -2567,6 +2583,7 @@ void validReportColumns (const std::vector <std::string>& columns)
*it != "age" &&
*it != "active" &&
*it != "tags" &&
*it != "recur" &&
*it != "description")
bad.push_back (*it);

View file

@ -126,7 +126,7 @@ void formatTimeDeltaDays (std::string&, time_t);
std::string formatSeconds (time_t);
const std::string uuid ();
const char* optionalBlankLine (Config&);
int convertDuration (std::string&);
int convertDuration (const std::string&);
std::string expandPath (const std::string&);
#ifdef SOLARIS

67
src/tests/recur.t Executable file
View file

@ -0,0 +1,67 @@
#! /usr/bin/perl
################################################################################
## task - a command line task list manager.
##
## Copyright 2006 - 2009, Paul Beckingham.
## All rights reserved.
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the
##
## Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor,
## Boston, MA
## 02110-1301
## USA
##
################################################################################
use strict;
use warnings;
use Test::More tests => 6;
# Create the rc file.
if (open my $fh, '>', 'recur.rc')
{
print $fh "data.location=.\n",
"report.asc.columns=id,recur,description\n",
"report.asc.sort=recur+\n",
"report.desc.columns=id,recur,description\n",
"report.desc.sort=recur-\n";
close $fh;
ok (-r 'recur.rc', 'Created recur.rc');
}
# Create a few recurring tasks, and test the sort order of the recur column.
qx{../task rc:recur.rc add due:tomorrow recur:daily first};
qx{../task rc:recur.rc add due:tomorrow recur:weekly second};
qx{../task rc:recur.rc add due:tomorrow recur:3d third};
my $output = qx{../task rc:recur.rc asc};
like ($output, qr/first .* third .* second/msx, 'daily 3d weekly');
$output = qx{../task rc:recur.rc desc};
like ($output, qr/second .* third .* first/msx, 'weekly 3d daily');
# Cleanup.
unlink 'shadow.txt';
ok (!-r 'shadow.txt', 'Removed shadow.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'recur.rc';
ok (!-r 'recur.rc', 'Removed recur.rc');
exit 0;

View file

@ -241,9 +241,9 @@ const std::string uuid ()
////////////////////////////////////////////////////////////////////////////////
// Recognize the following constructs, and return the number of days represented
int convertDuration (std::string& input)
int convertDuration (const std::string& input)
{
input = lowerCase (input);
std::string lower_input = lowerCase (input);
Date today;
std::vector <std::string> supported;
@ -263,7 +263,7 @@ int convertDuration (std::string& input)
supported.push_back ("yearly");
std::vector <std::string> matches;
if (autoComplete (input, supported, matches) == 1)
if (autoComplete (lower_input, supported, matches) == 1)
{
std::string found = matches[0];
@ -279,19 +279,18 @@ int convertDuration (std::string& input)
}
// Support \d+ d|w|m|q|y
else
{
// Verify all digits followed by d, w, m, q, or y.
unsigned int length = input.length ();
unsigned int length = lower_input.length ();
for (unsigned int i = 0; i < length; ++i)
{
if (! isdigit (input[i]) &&
if (! isdigit (lower_input[i]) &&
i == length - 1)
{
int number = ::atoi (input.substr (0, i).c_str ());
int number = ::atoi (lower_input.substr (0, i).c_str ());
switch (input[length - 1])
switch (lower_input[length - 1])
{
case 'd': return number * 1; break;
case 'w': return number * 7; break;