mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
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:
parent
751094cffb
commit
17de9fec9f
9 changed files with 125 additions and 12 deletions
|
@ -29,6 +29,8 @@
|
||||||
to help ensure higher quality releases.
|
to help ensure higher quality releases.
|
||||||
+ Fixed bug that caused performance hit during table rendering.
|
+ Fixed bug that caused performance hit during table rendering.
|
||||||
+ Fixed bug that concatenated a modified description without spaces.
|
+ Fixed bug that concatenated a modified description without spaces.
|
||||||
|
+ Added new column 'recur' that displays the recurrence period of any
|
||||||
|
recurring tasks. This column can be added to any custom report.
|
||||||
|
|
||||||
------ old releases ------------------------------
|
------ old releases ------------------------------
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ report.mine.sort=priority-,project+</pre></code>
|
||||||
<li>age
|
<li>age
|
||||||
<li>active
|
<li>active
|
||||||
<li>tags
|
<li>tags
|
||||||
|
<li>recur
|
||||||
<li>description
|
<li>description
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,8 @@
|
||||||
to help ensure higher quality releases.
|
to help ensure higher quality releases.
|
||||||
<li>Fixed bug that caused large performance hit during table rendering.
|
<li>Fixed bug that caused large performance hit during table rendering.
|
||||||
<li>Fixed bug that concatenated a modified description without spaces.
|
<li>Fixed bug that concatenated a modified description without spaces.
|
||||||
|
<li>Added new column 'recur' that displays the recurrence period of any
|
||||||
|
recurring tasks. This column can be added to any custom report.
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -941,6 +941,24 @@ void Table::sort (std::vector <int>& order)
|
||||||
((std::string)*left == "M" && (std::string)*right == "H"))
|
((std::string)*left == "M" && (std::string)*right == "H"))
|
||||||
SWAP
|
SWAP
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/Table.h
13
src/Table.h
|
@ -37,9 +37,16 @@ class Table
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum just {left, center, right};
|
enum just {left, center, right};
|
||||||
enum order {ascendingNumeric, ascendingCharacter, ascendingPriority,
|
enum order {ascendingNumeric,
|
||||||
ascendingDate, descendingNumeric, descendingCharacter,
|
ascendingCharacter,
|
||||||
descendingPriority, descendingDate};
|
ascendingPriority,
|
||||||
|
ascendingDate,
|
||||||
|
ascendingPeriod,
|
||||||
|
descendingNumeric,
|
||||||
|
descendingCharacter,
|
||||||
|
descendingPriority,
|
||||||
|
descendingDate,
|
||||||
|
descendingPeriod};
|
||||||
enum sizing {minimum = -1, flexible = 0};
|
enum sizing {minimum = -1, flexible = 0};
|
||||||
|
|
||||||
Table ();
|
Table ();
|
||||||
|
|
|
@ -2447,6 +2447,16 @@ std::string handleCustomReport (
|
||||||
table.addCell (row, columnCount, tasks[row].getDescription ());
|
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.
|
// Common to all columns.
|
||||||
// Add underline.
|
// Add underline.
|
||||||
if (conf.get (std::string ("color"), true) || conf.get (std::string ("_forcecolor"), false))
|
if (conf.get (std::string ("color"), true) || conf.get (std::string ("_forcecolor"), false))
|
||||||
|
@ -2487,6 +2497,12 @@ std::string handleCustomReport (
|
||||||
Table::ascendingDate :
|
Table::ascendingDate :
|
||||||
Table::descendingDate));
|
Table::descendingDate));
|
||||||
|
|
||||||
|
else if (column == "recur")
|
||||||
|
table.sortOn (columnIndex[column],
|
||||||
|
(direction == '+' ?
|
||||||
|
Table::ascendingPeriod :
|
||||||
|
Table::descendingPeriod));
|
||||||
|
|
||||||
else
|
else
|
||||||
table.sortOn (columnIndex[column],
|
table.sortOn (columnIndex[column],
|
||||||
(direction == '+' ?
|
(direction == '+' ?
|
||||||
|
@ -2567,6 +2583,7 @@ void validReportColumns (const std::vector <std::string>& columns)
|
||||||
*it != "age" &&
|
*it != "age" &&
|
||||||
*it != "active" &&
|
*it != "active" &&
|
||||||
*it != "tags" &&
|
*it != "tags" &&
|
||||||
|
*it != "recur" &&
|
||||||
*it != "description")
|
*it != "description")
|
||||||
bad.push_back (*it);
|
bad.push_back (*it);
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ void formatTimeDeltaDays (std::string&, time_t);
|
||||||
std::string formatSeconds (time_t);
|
std::string formatSeconds (time_t);
|
||||||
const std::string uuid ();
|
const std::string uuid ();
|
||||||
const char* optionalBlankLine (Config&);
|
const char* optionalBlankLine (Config&);
|
||||||
int convertDuration (std::string&);
|
int convertDuration (const std::string&);
|
||||||
std::string expandPath (const std::string&);
|
std::string expandPath (const std::string&);
|
||||||
|
|
||||||
#ifdef SOLARIS
|
#ifdef SOLARIS
|
||||||
|
|
67
src/tests/recur.t
Executable file
67
src/tests/recur.t
Executable 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;
|
||||||
|
|
15
src/util.cpp
15
src/util.cpp
|
@ -241,9 +241,9 @@ const std::string uuid ()
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Recognize the following constructs, and return the number of days represented
|
// 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;
|
Date today;
|
||||||
|
|
||||||
std::vector <std::string> supported;
|
std::vector <std::string> supported;
|
||||||
|
@ -263,7 +263,7 @@ int convertDuration (std::string& input)
|
||||||
supported.push_back ("yearly");
|
supported.push_back ("yearly");
|
||||||
|
|
||||||
std::vector <std::string> matches;
|
std::vector <std::string> matches;
|
||||||
if (autoComplete (input, supported, matches) == 1)
|
if (autoComplete (lower_input, supported, matches) == 1)
|
||||||
{
|
{
|
||||||
std::string found = matches[0];
|
std::string found = matches[0];
|
||||||
|
|
||||||
|
@ -279,19 +279,18 @@ int convertDuration (std::string& input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support \d+ d|w|m|q|y
|
// Support \d+ d|w|m|q|y
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Verify all digits followed by d, w, m, q, or y.
|
// 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)
|
for (unsigned int i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
if (! isdigit (input[i]) &&
|
if (! isdigit (lower_input[i]) &&
|
||||||
i == length - 1)
|
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 'd': return number * 1; break;
|
||||||
case 'w': return number * 7; break;
|
case 'w': return number * 7; break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue