From 751e8e7f90013dcf5e3bd9b1d5511b1e63480b85 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 5 Sep 2011 01:11:37 -0400 Subject: [PATCH] Import YAML - Implemented the import-yaml.pl external script. - Added unit tests. - Fixed problem where the pending tasks were not loaded prior to uuid verification. --- scripts/add-ons/import-yaml.pl | 143 +++++++++++++++++++++++++++++++++ src/TDB2.cpp | 8 +- src/commands/CmdImport.cpp | 17 ++-- test/import.yaml.t | 83 ++++++++++--------- 4 files changed, 195 insertions(+), 56 deletions(-) create mode 100755 scripts/add-ons/import-yaml.pl diff --git a/scripts/add-ons/import-yaml.pl b/scripts/add-ons/import-yaml.pl new file mode 100755 index 000000000..cb3bb3a1b --- /dev/null +++ b/scripts/add-ons/import-yaml.pl @@ -0,0 +1,143 @@ +#! /usr/bin/perl +################################################################################ +## taskwarrior - a command line task list manager. +## +## Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez. +## 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 Time::Local; + +my @tasks; + +my $status = ''; +my $uuid = ''; +my $priority = ''; +my $entry = ''; +my $end = ''; +my $project = ''; +my $tags = ''; +my $description = ''; +my $due = ''; + +my %annotations; +my $mid_anno = 0; +my $anno_entry = ''; +my $anno_desc = ''; + +while (my $yaml = <>) +{ + chomp $yaml; + next if $yaml =~ /\sid:/; + next if $yaml =~ /\sannotations:/; + + if ($yaml =~ /task:|\.\.\./ && $description ne '') + { + # Compose the JSON + my $json = "{\"status\":\"${status}\""; + $json .= ",\"uuid\":\"${uuid}\"" if $uuid ne ''; + $json .= ",\"priority\":\"${priority}\"" if $priority ne ''; + $json .= ",\"project\":\"${project}\"" if $project ne ''; + $json .= ",\"entry\":\"${entry}\"" if $entry ne ''; + $json .= ",\"end\":\"${end}\"" if $end ne ''; + $json .= ",\"due\":\"${due}\"" if $due ne ''; + $json .= ",\"tags\":\"${tags}\"" if $tags ne ''; + $json .= ",\"description\":\"${description}\"}"; + + for my $key (sort keys %annotations) + { + $json .= ",\"${key}\":\"" . $annotations{$key} . "\"" + } + + push @tasks, $json; + + $status = $uuid = $priority = $entry = $end = $project = $tags = + $description = $due = $anno_entry = $anno_desc = ''; + $mid_anno = 0; + %annotations = (); + $yaml = ''; + } + else + { + $mid_anno = 1 if $yaml =~ /annotation:/; + $anno_entry = $1 if $mid_anno && $yaml =~ /entry:\s*(\S+)/; + if ($mid_anno) + { + if ($yaml =~ /description:\s*(.+)/) + { + $anno_desc = $1; + $mid_anno = 0; + + $annotations{'annotation_' . epoch ($anno_entry)} = $anno_desc; + } + } + else + { + $entry = $1 if $yaml =~ /entry:\s*(\S+)/; + $description = $1 if $yaml =~ /description:\s*(.+)/; + } + + $status = $1 if $yaml =~ /status:\s*(\S+)/; + $uuid = $1 if $yaml =~ /uuid:\s*(\S+)/; + $priority = $1 if $yaml =~ /priority:\s*(\S+)/; + $project = $1 if $yaml =~ /project:\s*(\S+)/; + $end = $1 if $yaml =~ /end:\s*(\S+)/; + $due = $1 if $yaml =~ /due:\s*(\S+)/; + $tags = $1 if $yaml =~ /tags:\s*(\S+)/; + } +} + +print "[\n", join (",\n", @tasks), "\n]\n"; +exit 0; + +################################################################################ +sub epoch +{ + my ($input) = @_; + + my ($Y, $M, $D, $h, $m, $s) = $input =~ /(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/; + + return timegm ($s, $m, $h, $D, $M-1, $Y-1900); +} + +################################################################################ + +__DATA__ +%YAML 1.1 +--- + task: + annotations: + annotation: + entry: 20100706T025311Z + description: Also needs to ignore control codes + description: text.cpp/extractLines needs to calculate string length in a way that supports UTF8 + entry: 20090319T151633Z + id: 9 + project: task-2.0 + status: pending + tags: bug,utf8,next + uuid: 0c4cf066-9413-4862-9dc8-0793f158a649 +... + diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 5332ba433..1c3286300 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -1771,13 +1771,11 @@ int TDB2::id (const std::string& uuid) } //////////////////////////////////////////////////////////////////////////////// +// Make sure the specified UUID does not already exist in the data. bool TDB2::verifyUniqueUUID (const std::string& uuid) { - if (pending.id (uuid) != 0 || - completed.id (uuid) != 0) - return false; - - return true; + pending.get_tasks (); + return pending.id (uuid) != 0 ? false : true; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 62dbd66f3..fc76c5f67 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -174,19 +174,12 @@ int CmdImport::execute (std::string& output) task.setAnnotations (annos); } - // TODO Implement - else if (i->first == "parent") - { - } - - // TODO Implement - else if (i->first == "mask") - { - } - - // TODO Implement - else if (i->first == "imask") + // Attributes without columns are simply added. + else if (i->first == "parent" || + i->first == "mask" || + i->first == "imask") { + task.set (i->first, unquoteText (i->second->dump ())); } else diff --git a/test/import.yaml.t b/test/import.yaml.t index 02309a44c..c5f3985e5 100755 --- a/test/import.yaml.t +++ b/test/import.yaml.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 20; +use Test::More tests => 16; # Create the rc file. if (open my $fh, '>', 'import.rc') @@ -49,19 +49,19 @@ if (open my $fh, '>', 'import.txt') description: zero project: A status: pending - entry: 1234567889 + entry: 20110901T120000Z task: uuid: 11111111-1111-1111-1111-111111111111 description: one project: B status: pending - entry: 1234567889 + entry: 20110902T120000Z task: uuid: 22222222-2222-2222-2222-222222222222 description: two status: completed - entry: 1234524689 - end: 1234524690 + entry: 20110903T010000Z + end: 20110904T120000Z ... EOF @@ -69,9 +69,12 @@ EOF ok (-r 'import.txt', 'Created sample import data'); } -my $output = qx{../src/task rc:import.rc import import.txt}; -like ($output, qr/Imported 3 tasks successfully./, 'no errors'); -# Imported 3 tasks successfully. +# Convert YAML --> task JSON. +qx{../scripts/add-ons/import-yaml.pl import.json}; + +# Import the JSON. +my $output = qx{../src/task rc:import.rc import import.json}; +like ($output, qr/Imported 3 tasks\./, '3 tasks imported'); $output = qx{../src/task rc:import.rc list}; # ID Project Pri Due Active Age Description @@ -94,55 +97,57 @@ $output = qx{../src/task rc:import.rc completed}; unlike ($output, qr/1.+A.+zero/, 't1 missing'); unlike ($output, qr/2.+B.+one/, 't2 missing'); -like ($output, qr/2\/13\/2009.+two/, 't3 present'); +like ($output, qr/9\/4\/2011.+two/, 't3 present'); # Make sure that a duplicate task cannot be imported. -$output = qx{../src/task rc:import.rc import import.txt}; -like ($output, qr/Cannot add task because the uuid .+ is not unique\./, 'error on duplicate uuid'); +$output = qx{../src/task rc:import.rc import import.json}; +like ($output, qr/Cannot add task because the uuid '.{36}' is not unique\./, 'error on duplicate uuid'); # Create import file. -if (open my $fh, '>', 'import2.txt') +if (open my $fh, '>', 'import.txt') { print $fh < task JSON. +qx{../scripts/add-ons/import-yaml.pl import.json}; + +# Import the JSON. +$output = qx{../src/task rc:import.rc import import.json}; +like ($output, qr/Imported 1 tasks\./, '1 task imported'); + +# Verify. +$output = qx{../src/task rc:import.rc list}; +# ID Project Pri Due Active Age Description +# -- ------- --- --- ------ ---- ----------- +# 3 2.6y three +# 1 A 3d zero +# 2 B 2d one +like ($output, qr/1.+A.+zero/, 't1 present'); +like ($output, qr/2.+B.+one/, 't2 present'); +like ($output, qr/3.+three/, 't3 present'); # Cleanup. -unlink 'import.txt'; -ok (!-r 'import.txt', 'Removed import.txt'); - -unlink 'import2.txt'; -ok (!-r 'import2.txt', 'Removed import2.txt'); - -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'completed.data'; -ok (!-r 'completed.data', 'Removed completed.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'backlog.data'; -ok (!-r 'backlog.data', 'Removed backlog.data'); - -unlink 'synch.key'; -ok (!-r 'synch.key', 'Removed synch.key'); - -unlink 'import.rc'; -ok (!-r 'import.rc', 'Removed import.rc'); - +unlink qw(pending.data completed.data undo.data backlog.data synch.key import.rc import.txt import.json); +ok (! -r 'pending.data' && + ! -r 'completed.data' && + ! -r 'undo.data' && + ! -r 'backlog.data' && + ! -r 'synch_key.data' && + ! -r 'import.rc' && + ! -r 'import.txt' && + ! -r 'import.json', 'Cleanup'); exit 0;