From 99dc72f26f27121177ffcd88603c8760f06e361e Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Thu, 26 Mar 2009 00:41:15 -0400 Subject: [PATCH] File Import - Added format identifier code for task 1.4.3, task 1.5.0, todo.sh 2.0 and CSV. - Implemented import for type text. - Implemented util.cpp:slurp function. - Gathered sample input files for import testing, and later, unit tests. --- src/import.cpp | 222 +++++++++++++++++++++++++++++++++----- src/import/freeform.csv | 4 + src/import/line.txt | 3 + src/import/task-1.4.3.csv | 2 + src/import/task-1.5.0.csv | 2 + src/import/todo.txt | 2 + src/task.h | 2 + src/util.cpp | 26 +++++ 8 files changed, 239 insertions(+), 24 deletions(-) create mode 100644 src/import/freeform.csv create mode 100644 src/import/line.txt create mode 100644 src/import/task-1.4.3.csv create mode 100644 src/import/task-1.5.0.csv create mode 100644 src/import/todo.txt diff --git a/src/import.cpp b/src/import.cpp index 50504c3c6..a6ccf26c8 100644 --- a/src/import.cpp +++ b/src/import.cpp @@ -28,24 +28,6 @@ #include #include "task.h" -//////////////////////////////////////////////////////////////////////////////// -std::string handleImport (TDB& tdb, T& task, Config& conf) -{ - std::stringstream out; - - // Use the description as a file name. - std::string file = trim (task.getDescription ()); - - if (file.length () > 0) - { - out << "Not yet implemented." << std::endl; - } - else - throw std::string ("You must specify a file to import."); - - return out.str (); -} - //////////////////////////////////////////////////////////////////////////////// // todo.sh v2.x // file format: (A) Walk the dog +project @context @@ -69,29 +51,221 @@ std::string handleImport (TDB& tdb, T& task, Config& conf) // file format: project,priority,description //////////////////////////////////////////////////////////////////////////////// -void determineFileType () +enum fileType { + not_a_clue, + task_1_4_3, + task_1_5_0, + todo_sh_2_0, + csv, + text +}; + +static fileType determineFileType (const std::vector & lines) +{ + // '7f7a4191-c2f2-487f-8855-7a1eb378c267',' ... + // ....:....|....:....|....:....|....:....| + // 1 10 20 30 40 + if (lines.size () > 1 && + lines[1][0] == '\'' && + lines[1][9] == '-' && + lines[1][14] == '-' && + lines[1][19] == '-' && + lines[1][24] == '-' && + lines[1][37] == '\'' && + lines[1][38] == ',' && + lines[1][39] == '\'') + { + if (lines[0] == "'id','uuid','status','tags','entry','start','due','recur'," + "'end','project','priority','fg','bg','description'") + return task_1_5_0; + + if (lines[0] == "'id','status','tags','entry','start','due','end','project'," + "'priority','fg','bg','description'") + return task_1_4_3; + } + + // x 2009-03-25 Walk the dog +project @context + // This is a test +project @context + for (unsigned int i = 0; i < lines.size (); ++i) + { + // All done tasks begin with "x YYYY-MM-DD". + if (lines[i].length () > 12) + { + if ( lines[i][0] == 'x' && + lines[i][1] == ' ' && + ::isdigit (lines[i][2]) && + ::isdigit (lines[i][3]) && + ::isdigit (lines[i][4]) && + ::isdigit (lines[i][5]) && + lines[i][6] == '-' && + ::isdigit (lines[i][7]) && + ::isdigit (lines[i][8]) && + lines[i][9] == '-' && + ::isdigit (lines[i][10]) && + ::isdigit (lines[i][11])) + return todo_sh_2_0; + } + + std::vector words; + split (words, lines[i], ' '); + for (unsigned int w = 0; w < words.size (); ++w) + { + // +project + if (words[w].length () > 1 && + words[w][0] == '+' && + ::isalnum (words[w][1])) + return todo_sh_2_0; + + // @context + if (words[w].length () > 1 && + words[w][0] == '@' && + ::isalnum (words[w][1])) + return todo_sh_2_0; + } + } + + // CSV - commas on every line. + bool commas_on_every_line = true; + for (unsigned int i = 0; i < lines.size (); ++i) + { + if (lines[i].length () > 10 && + lines[i][0] != '#' && + lines[i].find (",") == std::string::npos) + { + commas_on_every_line = false; + break; + } + } + if (commas_on_every_line) + return csv; + + // TODO text, possibly with commas. + for (unsigned int i = 0; i < lines.size (); ++i) + { + // 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; } //////////////////////////////////////////////////////////////////////////////// -void importTask_1_5 () +static std::string importTask_1_4_3 ( + TDB& tdb, + Config& conf, + const std::vector & lines) { + return "task 1.4.3\n"; } -void importTask_1_6 () +//////////////////////////////////////////////////////////////////////////////// +static std::string importTask_1_5_0 ( + TDB& tdb, + Config& conf, + const std::vector & lines) { + return "task 1.5.0\n"; } -void importTodoSh_2_0 () +//////////////////////////////////////////////////////////////////////////////// +static std::string importTodoSh_2_0 ( + TDB& tdb, + Config& conf, + const std::vector & lines) { + return "todo.sh 2.0\n"; } -void importSingleLine () +//////////////////////////////////////////////////////////////////////////////// +static std::string importText ( + TDB& tdb, + Config& conf, + const std::vector & lines) { + std::vector failed; + + for (unsigned int i = 0; i < lines.size (); ++i) + { + std::string line = lines[i]; + + // 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) + { + T task; + task.parse (std::string ("\"") + lines[i] + "\""); + if (! tdb.addT (task)) + failed.push_back (lines[i]); + } + } + + std::stringstream out; + out << "Imported " + << (lines.size () - failed.size ()) + << " tasks successfully, with " + << failed.size () + << " errors." + << std::endl; + + std::string bad; + join (bad, "\n", failed); + return out.str () + "\n" + bad; } -void importCSV () +//////////////////////////////////////////////////////////////////////////////// +static std::string importCSV ( + TDB& tdb, + Config& conf, + const std::vector & lines) { + return "CSV\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +std::string handleImport (TDB& tdb, T& task, Config& conf) +{ + std::stringstream out; + + // Use the description as a file name. + std::string file = trim (task.getDescription ()); + if (file.length () > 0) + { + // Load the file. + std::vector lines; + slurp (file, lines, true); + + // Determine which type it might be, then attempt an import. + switch (determineFileType (lines)) + { + 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 todo_sh_2_0: out << importTodoSh_2_0 (tdb, conf, lines); break; + case csv: out << importCSV (tdb, conf, lines); break; + case text: out << importText (tdb, conf, lines); break; + + case not_a_clue: + out << "?"; + break; + } + } + else + throw std::string ("You must specify a file to import."); + + return out.str (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/import/freeform.csv b/src/import/freeform.csv new file mode 100644 index 000000000..6d60bb5f6 --- /dev/null +++ b/src/import/freeform.csv @@ -0,0 +1,4 @@ +'id','priority','description' +1,H,'this is a test' +2,,'another task' + diff --git a/src/import/line.txt b/src/import/line.txt new file mode 100644 index 000000000..3ac276232 --- /dev/null +++ b/src/import/line.txt @@ -0,0 +1,3 @@ +This is a test priority:H project:A +Another task + diff --git a/src/import/task-1.4.3.csv b/src/import/task-1.4.3.csv new file mode 100644 index 000000000..b917e0df9 --- /dev/null +++ b/src/import/task-1.4.3.csv @@ -0,0 +1,2 @@ +'id','status','tags','entry','start','due','end','project','priority','fg','bg','description' +'545629d2-24a3-4a32-8894-57e708b98354','pending','',1238037900,,,,'A','M',,,'foo bar ' diff --git a/src/import/task-1.5.0.csv b/src/import/task-1.5.0.csv new file mode 100644 index 000000000..08c900aa9 --- /dev/null +++ b/src/import/task-1.5.0.csv @@ -0,0 +1,2 @@ +'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' diff --git a/src/import/todo.txt b/src/import/todo.txt new file mode 100644 index 000000000..8177b0c40 --- /dev/null +++ b/src/import/todo.txt @@ -0,0 +1,2 @@ +x 2009-03-25 Walk the dog +project @context +This is a test +project @context diff --git a/src/task.h b/src/task.h index 788ae26ca..2a0b8f59f 100644 --- a/src/task.h +++ b/src/task.h @@ -139,6 +139,8 @@ std::string expandPath (const std::string&); int flock (int, int); #endif +bool slurp (const std::string&, std::vector &, bool trimLines = false); + // rules.cpp void initializeColorRules (Config&); void autoColorize (T&, Text::color&, Text::color&, Config&); diff --git a/src/util.cpp b/src/util.cpp index a506377af..1fe260c89 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -25,6 +25,7 @@ // //////////////////////////////////////////////////////////////////////////////// #include +#include #include #include #include @@ -404,3 +405,28 @@ int flock (int fd, int operation) #endif //////////////////////////////////////////////////////////////////////////////// +bool slurp ( + const std::string& file, + std::vector & contents, + bool trimLines /* = false */) +{ + contents.clear (); + + std::ifstream in (file.c_str ()); + if (in.good ()) + { + std::string line; + while (getline (in, line)) + { + if (trimLines) line = trim (line); + contents.push_back (line); + } + + in.close (); + return true; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////