File Import

- Implemented import from task 1.4.3, 1.5.0, 1.6.0, plain text,
  todo.sh and task command line files.
- Implemented unit tests to cover the above.
This commit is contained in:
Paul Beckingham 2009-03-29 15:34:35 -04:00
parent c1291dc587
commit 5f4563af2f
16 changed files with 1180 additions and 54 deletions

View file

@ -24,6 +24,8 @@
export files from versions 1.4.3 and earlier, versions 1.5.0 and later, export files from versions 1.4.3 and earlier, versions 1.5.0 and later,
todo.sh 2.x, CSV, plain text and task command line. See online docs for todo.sh 2.x, CSV, plain text and task command line. See online docs for
full details. full details.
+ Export was including 'id' in the column header even though it was not
included in the data.
------ old releases ------------------------------ ------ old releases ------------------------------

View file

@ -121,6 +121,8 @@
export files from versions 1.4.3 and earlier, versions 1.5.0 and later, export files from versions 1.4.3 and earlier, versions 1.5.0 and later,
todo.sh 2.x, CSV, plain text and task command line. See online docs for todo.sh 2.x, CSV, plain text and task command line. See online docs for
full details. full details.
<li>Export was including 'id' in the column header even though it was not
included in the data.
</ul> </ul>
<p> <p>

View file

@ -26,6 +26,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "Date.h"
#include "task.h" #include "task.h"
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -34,6 +35,7 @@ enum fileType
not_a_clue, not_a_clue,
task_1_4_3, task_1_4_3,
task_1_5_0, task_1_5_0,
task_1_6_0,
task_cmd_line, task_cmd_line,
todo_sh_2_0, todo_sh_2_0,
csv, csv,
@ -55,6 +57,10 @@ static fileType determineFileType (const std::vector <std::string>& lines)
lines[1][38] == ',' && lines[1][38] == ',' &&
lines[1][39] == '\'') lines[1][39] == '\'')
{ {
if (lines[0] == "'uuid','status','tags','entry','start','due','recur',"
"'end','project','priority','fg','bg','description'")
return task_1_6_0;
if (lines[0] == "'id','uuid','status','tags','entry','start','due','recur'," if (lines[0] == "'id','uuid','status','tags','entry','start','due','recur',"
"'end','project','priority','fg','bg','description'") "'end','project','priority','fg','bg','description'")
return task_1_5_0; return task_1_5_0;
@ -64,7 +70,26 @@ static fileType determineFileType (const std::vector <std::string>& lines)
return task_1_4_3; return task_1_4_3;
} }
// TODO task_cmd_line // A task command line might include a priority or project.
for (unsigned int i = 0; i < lines.size (); ++i)
{
std::vector <std::string> words;
split (words, lines[i], ' ');
for (unsigned int w = 0; w < words.size (); ++w)
if (words[w].substr (0, 9) == "priority:" ||
words[w].substr (0, 8) == "priorit:" ||
words[w].substr (0, 7) == "priori:" ||
words[w].substr (0, 6) == "prior:" ||
words[w].substr (0, 5) == "prio:" ||
words[w].substr (0, 4) == "pri:" ||
words[w].substr (0, 8) == "project:" ||
words[w].substr (0, 7) == "projec:" ||
words[w].substr (0, 6) == "proje:" ||
words[w].substr (0, 5) == "proj:" ||
words[w].substr (0, 4) == "pro:")
return task_cmd_line;
}
// x 2009-03-25 Walk the dog +project @context // x 2009-03-25 Walk the dog +project @context
// This is a test +project @context // This is a test +project @context
@ -111,7 +136,6 @@ static fileType determineFileType (const std::vector <std::string>& lines)
for (unsigned int i = 0; i < lines.size (); ++i) for (unsigned int i = 0; i < lines.size (); ++i)
{ {
if (lines[i].length () > 10 && if (lines[i].length () > 10 &&
lines[i][0] != '#' &&
lines[i].find (",") == std::string::npos) lines[i].find (",") == std::string::npos)
{ {
commas_on_every_line = false; commas_on_every_line = false;
@ -121,32 +145,187 @@ static fileType determineFileType (const std::vector <std::string>& lines)
if (commas_on_every_line) if (commas_on_every_line)
return csv; return csv;
// TODO text, possibly with commas. // Looks like 'text' is the default case, if there is any data at all.
for (unsigned int i = 0; i < lines.size (); ++i) if (lines.size () > 1)
{ return text;
// TODO priority:{H,M,L}
// TODO priorit:{H,M,L}
// TODO priori:{H,M,L}
// TODO prior:{H,M,L}
// TODO prio:{H,M,L}
// TODO pri:{H,M,L}
// TODO project:.+
// TODO projec:.+
// TODO proje:.+
// TODO proj:.+
// TODO pro:.+
}
return not_a_clue; return not_a_clue;
} }
////////////////////////////////////////////////////////////////////////////////
static void decorateTask (T& task, Config& conf)
{
char entryTime[16];
sprintf (entryTime, "%u", (unsigned int) time (NULL));
task.setAttribute ("entry", entryTime);
// Override with default.project, if not specified.
std::string defaultProject = conf.get ("default.project", "");
if (task.getAttribute ("project") == "" && defaultProject != "")
task.setAttribute ("project", defaultProject);
// Override with default.priority, if not specified.
std::string defaultPriority = conf.get ("default.priority", "");
if (task.getAttribute ("priority") == "" &&
defaultPriority != "" &&
validPriority (defaultPriority))
task.setAttribute ("priority", defaultPriority);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static std::string importTask_1_4_3 ( static std::string importTask_1_4_3 (
TDB& tdb, TDB& tdb,
Config& conf, Config& conf,
const std::vector <std::string>& lines) const std::vector <std::string>& lines)
{ {
return "task 1.4.3\n"; std::vector <std::string> failed;
std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{
try
{
// Skip the first line, if it is a columns header line.
if (it->substr (0, 5) == "'id',")
continue;
std::vector <std::string> fields;
split (fields, *it, ',');
// If there is an unexpected number of fields, something is wrong. Perhaps
// an embedded comma, in which case there are (at least) two fields that
// need to be concatenated.
if (fields.size () > 12)
{
int safety = 10; // Shouldn't be more than 10 commas in a description/project.
do
{
std::vector <std::string> modified;
for (unsigned int f = 0; f < fields.size (); ++f)
{
if (fields[f][0] != '\'' &&
fields[f][fields[f].length () - 1] == '\'')
{
modified[modified.size () - 1] += "," + fields[f];
}
else
modified.push_back (fields[f]);
}
fields = modified;
if (safety-- <= 0)
throw "unrecoverable";
}
while (fields.size () > 12);
}
if (fields.size () < 12)
throw "unrecoverable";
// Build up this task ready for insertion.
T task;
// Handle the 12 fields.
for (unsigned int f = 0; f < fields.size (); ++f)
{
switch (f)
{
case 0: // 'uuid'
task.setUUID (fields[f].substr (1, 36));
break;
case 1: // 'status'
if (fields[f] == "'pending'") task.setStatus (T::pending);
else if (fields[f] == "'recurring'") task.setStatus (T::recurring);
else if (fields[f] == "'deleted'") task.setStatus (T::deleted);
else if (fields[f] == "'completed'") task.setStatus (T::completed);
break;
case 2: // 'tags'
if (fields[f].length () > 2)
{
std::string tokens = fields[f].substr (1, fields[f].length () - 2);
std::vector <std::string> tags;
split (tags, tokens, ' ');
for (unsigned int i = 0; i > tags.size (); ++i)
task.addTag (tags[i]);
}
break;
case 3: // entry
task.setAttribute ("entry", fields[f]);
break;
case 4: // start
if (fields[f].length ())
task.setAttribute ("start", fields[f]);
break;
case 5: // due
if (fields[f].length ())
task.setAttribute ("due", fields[f]);
break;
case 6: // end
if (fields[f].length ())
task.setAttribute ("end", fields[f]);
break;
case 7: // 'project'
if (fields[f].length () > 2)
task.setAttribute ("project", fields[f].substr (1, fields[f].length () - 2));
break;
case 8: // 'priority'
if (fields[f].length () > 2)
task.setAttribute ("priority", fields[f].substr (1, fields[f].length () - 2));
break;
case 9: // 'fg'
if (fields[f].length () > 2)
task.setAttribute ("fg", fields[f].substr (1, fields[f].length () - 2));
break;
case 10: // 'bg'
if (fields[f].length () > 2)
task.setAttribute ("bg", fields[f].substr (1, fields[f].length () - 2));
break;
case 11: // 'description'
if (fields[f].length () > 2)
task.setDescription (fields[f].substr (1, fields[f].length () - 2));
break;
}
}
if (! tdb.addT (task))
failed.push_back (*it);
}
catch (...)
{
failed.push_back (*it);
}
}
std::stringstream out;
out << "Imported "
<< (lines.size () - failed.size () - 1)
<< " tasks successfully, with "
<< failed.size ()
<< " errors."
<< std::endl;
if (failed.size ())
{
std::string bad;
join (bad, "\n", failed);
return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -155,7 +334,320 @@ static std::string importTask_1_5_0 (
Config& conf, Config& conf,
const std::vector <std::string>& lines) const std::vector <std::string>& lines)
{ {
return "task 1.5.0\n"; std::vector <std::string> failed;
std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{
try
{
// Skip the first line, if it is a columns header line.
if (it->substr (0, 5) == "'id',")
continue;
std::vector <std::string> fields;
split (fields, *it, ',');
// If there is an unexpected number of fields, something is wrong. Perhaps
// an embedded comma, in which case there are (at least) two fields that
// need to be concatenated.
if (fields.size () > 13)
{
int safety = 10; // Shouldn't be more than 10 commas in a description/project.
do
{
std::vector <std::string> modified;
for (unsigned int f = 0; f < fields.size (); ++f)
{
if (fields[f][0] != '\'' &&
fields[f][fields[f].length () - 1] == '\'')
{
modified[modified.size () - 1] += "," + fields[f];
}
else
modified.push_back (fields[f]);
}
fields = modified;
if (safety-- <= 0)
throw "unrecoverable";
}
while (fields.size () > 13);
}
if (fields.size () < 13)
throw "unrecoverable";
// Build up this task ready for insertion.
T task;
// Handle the 13 fields.
for (unsigned int f = 0; f < fields.size (); ++f)
{
switch (f)
{
case 0: // 'uuid'
task.setUUID (fields[f].substr (1, 36));
break;
case 1: // 'status'
if (fields[f] == "'pending'") task.setStatus (T::pending);
else if (fields[f] == "'recurring'") task.setStatus (T::recurring);
else if (fields[f] == "'deleted'") task.setStatus (T::deleted);
else if (fields[f] == "'completed'") task.setStatus (T::completed);
break;
case 2: // 'tags'
if (fields[f].length () > 2)
{
std::string tokens = fields[f].substr (1, fields[f].length () - 2);
std::vector <std::string> tags;
split (tags, tokens, ' ');
for (unsigned int i = 0; i > tags.size (); ++i)
task.addTag (tags[i]);
}
break;
case 3: // entry
task.setAttribute ("entry", fields[f]);
break;
case 4: // start
if (fields[f].length ())
task.setAttribute ("start", fields[f]);
break;
case 5: // due
if (fields[f].length ())
task.setAttribute ("due", fields[f]);
break;
case 6: // recur
if (fields[f].length ())
task.setAttribute ("recur", fields[f]);
break;
case 7: // end
if (fields[f].length ())
task.setAttribute ("end", fields[f]);
break;
case 8: // 'project'
if (fields[f].length () > 2)
task.setAttribute ("project", fields[f].substr (1, fields[f].length () - 2));
break;
case 9: // 'priority'
if (fields[f].length () > 2)
task.setAttribute ("priority", fields[f].substr (1, fields[f].length () - 2));
break;
case 10: // 'fg'
if (fields[f].length () > 2)
task.setAttribute ("fg", fields[f].substr (1, fields[f].length () - 2));
break;
case 11: // 'bg'
if (fields[f].length () > 2)
task.setAttribute ("bg", fields[f].substr (1, fields[f].length () - 2));
break;
case 12: // 'description'
if (fields[f].length () > 2)
task.setDescription (fields[f].substr (1, fields[f].length () - 2));
break;
}
}
if (! tdb.addT (task))
failed.push_back (*it);
}
catch (...)
{
failed.push_back (*it);
}
}
std::stringstream out;
out << "Imported "
<< (lines.size () - failed.size () - 1)
<< " tasks successfully, with "
<< failed.size ()
<< " errors."
<< std::endl;
if (failed.size ())
{
std::string bad;
join (bad, "\n", failed);
return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
static std::string importTask_1_6_0 (
TDB& tdb,
Config& conf,
const std::vector <std::string>& lines)
{
std::vector <std::string> failed;
std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{
try
{
// Skip the first line, if it is a columns header line.
if (it->substr (0, 7) == "'uuid',")
continue;
std::vector <std::string> fields;
split (fields, *it, ',');
// If there is an unexpected number of fields, something is wrong. Perhaps
// an embedded comma, in which case there are (at least) two fields that
// need to be concatenated.
if (fields.size () > 13)
{
int safety = 10; // Shouldn't be more than 10 commas in a description/project.
do
{
std::vector <std::string> modified;
for (unsigned int f = 0; f < fields.size (); ++f)
{
if (fields[f][0] != '\'' &&
fields[f][fields[f].length () - 1] == '\'')
{
modified[modified.size () - 1] += "," + fields[f];
}
else
modified.push_back (fields[f]);
}
fields = modified;
if (safety-- <= 0)
throw "unrecoverable";
}
while (fields.size () > 13);
}
if (fields.size () < 13)
throw "unrecoverable";
// Build up this task ready for insertion.
T task;
// Handle the 13 fields.
for (unsigned int f = 0; f < fields.size (); ++f)
{
switch (f)
{
case 0: // 'uuid'
task.setUUID (fields[f].substr (1, 36));
break;
case 1: // 'status'
if (fields[f] == "'pending'") task.setStatus (T::pending);
else if (fields[f] == "'recurring'") task.setStatus (T::recurring);
else if (fields[f] == "'deleted'") task.setStatus (T::deleted);
else if (fields[f] == "'completed'") task.setStatus (T::completed);
break;
case 2: // 'tags'
if (fields[f].length () > 2)
{
std::string tokens = fields[f].substr (1, fields[f].length () - 2);
std::vector <std::string> tags;
split (tags, tokens, ' ');
for (unsigned int i = 0; i > tags.size (); ++i)
task.addTag (tags[i]);
}
break;
case 3: // entry
task.setAttribute ("entry", fields[f]);
break;
case 4: // start
if (fields[f].length ())
task.setAttribute ("start", fields[f]);
break;
case 5: // due
if (fields[f].length ())
task.setAttribute ("due", fields[f]);
break;
case 6: // recur
if (fields[f].length ())
task.setAttribute ("recur", fields[f]);
break;
case 7: // end
if (fields[f].length ())
task.setAttribute ("end", fields[f]);
break;
case 8: // 'project'
if (fields[f].length () > 2)
task.setAttribute ("project", fields[f].substr (1, fields[f].length () - 2));
break;
case 9: // 'priority'
if (fields[f].length () > 2)
task.setAttribute ("priority", fields[f].substr (1, fields[f].length () - 2));
break;
case 10: // 'fg'
if (fields[f].length () > 2)
task.setAttribute ("fg", fields[f].substr (1, fields[f].length () - 2));
break;
case 11: // 'bg'
if (fields[f].length () > 2)
task.setAttribute ("bg", fields[f].substr (1, fields[f].length () - 2));
break;
case 12: // 'description'
if (fields[f].length () > 2)
task.setDescription (fields[f].substr (1, fields[f].length () - 2));
break;
}
}
if (! tdb.addT (task))
failed.push_back (*it);
}
catch (...)
{
failed.push_back (*it);
}
}
std::stringstream out;
out << "Imported "
<< (lines.size () - failed.size () - 1)
<< " tasks successfully, with "
<< failed.size ()
<< " errors."
<< std::endl;
if (failed.size ())
{
std::string bad;
join (bad, "\n", failed);
return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -163,43 +655,28 @@ static std::string importTaskCmdLine (
TDB& tdb, TDB& tdb,
Config& conf, Config& conf,
const std::vector <std::string>& lines) const std::vector <std::string>& lines)
{
return "task command line\n";
}
////////////////////////////////////////////////////////////////////////////////
static std::string importTodoSh_2_0 (
TDB& tdb,
Config& conf,
const std::vector <std::string>& lines)
{
return "todo.sh 2.0\n";
}
////////////////////////////////////////////////////////////////////////////////
static std::string importText (
TDB& tdb,
Config& conf,
const std::vector <std::string>& lines)
{ {
std::vector <std::string> failed; std::vector <std::string> failed;
for (unsigned int i = 0; i < lines.size (); ++i) std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{ {
std::string line = lines[i]; std::string line = *it;
// Strip comments try
std::string::size_type pound = line.find ("#");
if (pound != std::string::npos)
line = line.substr (0, pound);
// Skip blank lines
if (line.length () > 0)
{ {
std::vector <std::string> args;
split (args, std::string ("add ") + line, ' ');
T task; T task;
task.parse (std::string ("\"") + lines[i] + "\""); std::string command;
if (! tdb.addT (task)) parse (args, command, task, conf);
failed.push_back (lines[i]); handleAdd (tdb, task, conf);
}
catch (...)
{
failed.push_back (line);
} }
} }
@ -211,9 +688,198 @@ static std::string importText (
<< " errors." << " errors."
<< std::endl; << std::endl;
if (failed.size ())
{
std::string bad; std::string bad;
join (bad, "\n", failed); join (bad, "\n", failed);
return out.str () + "\n" + bad; return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
static std::string importTodoSh_2_0 (
TDB& tdb,
Config& conf,
const std::vector <std::string>& lines)
{
std::vector <std::string> failed;
std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{
try
{
std::vector <std::string> args;
args.push_back ("add");
bool isPending = true;
Date endDate;
std::vector <std::string> words;
split (words, *it, ' ');
for (unsigned int w = 0; w < words.size (); ++w)
{
if (words[w].length () > 1 &&
words[w][0] == '+')
{
args.push_back (std::string ("project:") +
words[w].substr (1, std::string::npos));
}
// Convert "+aaa" to "project:aaa".
// Convert "@aaa" to "+aaa".
else if (words[w].length () > 1 &&
words[w][0] == '@')
{
args.push_back (std::string ("+") +
words[w].substr (1, std::string::npos));
}
// Convert "(A)" to "priority:H".
// Convert "(B)" to "priority:M".
// Convert "(?)" to "priority:L".
else if (words[w].length () == 3 &&
words[w][0] == '(' &&
words[w][2] == ')')
{
if (words[w][1] == 'A') args.push_back ("priority:H");
else if (words[w][1] == 'B') args.push_back ("priority:M");
else args.push_back ("priority:L");
}
// Set status, if completed.
else if (w == 0 &&
words[w] == "x")
{
isPending = false;
}
// Set status, and add an end date, if completed.
else if (! isPending &&
w == 1 &&
words[w].length () == 10 &&
words[w][4] == '-' &&
words[w][7] == '-')
{
endDate = Date (words[w], "Y-M-D");
}
// Just an ordinary word.
else
{
args.push_back (words[w]);
}
}
T task;
std::string command;
parse (args, command, task, conf);
decorateTask (task, conf);
if (isPending)
{
task.setStatus (T::pending);
}
else
{
task.setStatus (T::completed);
char end[16];
sprintf (end, "%u", (unsigned int) endDate.toEpoch ());
task.setAttribute ("end", end);
}
if (! tdb.addT (task))
failed.push_back (*it);
}
catch (...)
{
failed.push_back (*it);
}
}
std::stringstream out;
out << "Imported "
<< (lines.size () - failed.size ())
<< " tasks successfully, with "
<< failed.size ()
<< " errors."
<< std::endl;
if (failed.size ())
{
std::string bad;
join (bad, "\n", failed);
return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
static std::string importText (
TDB& tdb,
Config& conf,
const std::vector <std::string>& lines)
{
std::vector <std::string> failed;
int count = 0;
std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it)
{
std::string line = *it;
// Strip comments
std::string::size_type pound = line.find ("#");
if (pound != std::string::npos)
line = line.substr (0, pound);
// Skip blank lines
if (line.length () > 0)
{
try
{
++count;
std::vector <std::string> args;
split (args, std::string ("add ") + line, ' ');
T task;
std::string command;
parse (args, command, task, conf);
decorateTask (task, conf);
if (! tdb.addT (task))
failed.push_back (*it);
}
catch (...)
{
failed.push_back (line);
}
}
}
std::stringstream out;
out << "Imported "
<< count
<< " tasks successfully, with "
<< failed.size ()
<< " errors."
<< std::endl;
if (failed.size ())
{
std::string bad;
join (bad, "\n", failed);
return out.str () + "\nCould not import:\n\n" + bad;
}
return out.str ();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -237,8 +903,26 @@ std::string handleImport (TDB& tdb, T& task, Config& conf)
if (file.length () > 0) if (file.length () > 0)
{ {
// Load the file. // Load the file.
std::vector <std::string> all;
slurp (file, all, true);
std::vector <std::string> lines; std::vector <std::string> lines;
slurp (file, lines, true); std::vector <std::string>::iterator it;
for (it = all.begin (); it != all.end (); ++it)
{
std::string line = *it;
// Strip comments
std::string::size_type pound = line.find ("#");
if (pound != std::string::npos)
line = line.substr (0, pound);
trim (line);
// Skip blank lines
if (line.length () > 0)
lines.push_back (line);
}
// Take a guess at the file type. // Take a guess at the file type.
fileType type = determineFileType (lines); fileType type = determineFileType (lines);
@ -250,6 +934,7 @@ std::string handleImport (TDB& tdb, T& task, Config& conf)
{ {
case task_1_4_3: out << importTask_1_4_3 (tdb, conf, lines); break; case task_1_4_3: out << importTask_1_4_3 (tdb, conf, lines); break;
case task_1_5_0: out << importTask_1_5_0 (tdb, conf, lines); break; case task_1_5_0: out << importTask_1_5_0 (tdb, conf, lines); break;
case task_1_6_0: out << importTask_1_6_0 (tdb, conf, lines); break;
case task_cmd_line: out << importTaskCmdLine (tdb, conf, lines); break; case task_cmd_line: out << importTaskCmdLine (tdb, conf, lines); break;
case todo_sh_2_0: out << importTodoSh_2_0 (tdb, conf, lines); break; case todo_sh_2_0: out << importTodoSh_2_0 (tdb, conf, lines); break;
case csv: out << importCSV (tdb, conf, lines); break; case csv: out << importCSV (tdb, conf, lines); break;

View file

@ -1,2 +1,3 @@
'id','status','tags','entry','start','due','end','project','priority','fg','bg','description' 'id','status','tags','entry','start','due','end','project','priority','fg','bg','description'
'545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo bar ' '545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo bar '
'545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo, bar '

1 'id' 'id','status','tags','entry','start','due','end','project','priority','fg','bg','description' 'status' 'tags' 'entry' 'start' 'due' 'end' 'project' 'priority' 'fg' 'bg' 'description'
2 '545629d2-24a3-4a32-8894-57e708b98354' '545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo bar ' 'pending' '' 1238037900 'A' 'M' 'foo bar '
3 '545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo, bar '

View file

@ -1,2 +1,3 @@
'id','uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description' 'id','uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'
'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar' '7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'
'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'

1 'id','uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'
2 '7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'
3 '7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'

View file

@ -0,0 +1,3 @@
'uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'
'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'
'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'
1 'uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'
2 '7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'
3 '7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'

4
src/import/text.txt Normal file
View file

@ -0,0 +1,4 @@
Get milk, bread
Order cake
Clean house

View file

@ -1,2 +1,4 @@
x 2009-03-25 Walk the dog +project @context x 2009-03-25 Walk the dog +project @context
This is a test +project @context This is a test +project @context
(A) A prioritized task

View file

@ -235,7 +235,7 @@ bool validDate (std::string& date, Config& conf)
{ {
Date test (date, conf.get ("dateformat", "m/d/Y")); Date test (date, conf.get ("dateformat", "m/d/Y"));
char epoch[12]; char epoch[16];
sprintf (epoch, "%d", (int) test.toEpoch ()); sprintf (epoch, "%d", (int) test.toEpoch ());
date = epoch; date = epoch;

70
src/tests/import.143.t Executable file
View file

@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'id','status','tags','entry','start','due','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

70
src/tests/import.150.t Executable file
View file

@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'id','uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

70
src/tests/import.160.t Executable file
View file

@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

69
src/tests/import.cmd.t Executable file
View file

@ -0,0 +1,69 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "This is a test priority:H project:A\n",
"Another task\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+H.+This is a test/, 't1');
like ($output, qr/2.+Another task/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

76
src/tests/import.todo.t Executable file
View file

@ -0,0 +1,76 @@
#! /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 => 10;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "x 2010-03-25 Walk the dog +project \@context\n",
"This is a test +project \@context\n",
"(A) A prioritized task\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 3 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+project.+This is a test/, 't1');
like ($output, qr/2.+H.+A prioritized task/, 't2');
$output = qx{../task rc:import.rc completed};
like ($output, qr/3\/25\/2009.+Walk the dog/, 't3');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

71
src/tests/import.txt.t Executable file
View file

@ -0,0 +1,71 @@
#! /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 => 9;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "Get milk, bread\n",
"Order cake\n",
"Clean house\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 3 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+Get milk, bread/, 't1');
like ($output, qr/2.+Order cake/, 't2');
like ($output, qr/3.+Clean house/, 't3');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;