From 5988f25af913e07c284f3ba0638eca43f8242651 Mon Sep 17 00:00:00 2001 From: Andrew Chilton Date: Mon, 30 Jun 2008 00:15:05 +1200 Subject: [PATCH] Added the 'am' command (closes #48eaec49). --- README | 9 ++ bin/cil | 296 +++++++++++++++++++++++++++++++----------- debian-etch/control | 2 +- debian-lenny/control | 2 +- issues/c_45cd5e23.cil | 23 ++++ issues/c_6f5bc459.cil | 18 +++ issues/i_48eaec49.cil | 6 +- 7 files changed, 273 insertions(+), 83 deletions(-) create mode 100644 issues/c_45cd5e23.cil create mode 100644 issues/c_6f5bc459.cil diff --git a/README b/README index c0f3d5e..5feb695 100644 --- a/README +++ b/README @@ -81,6 +81,15 @@ it from the issue: $ cil extract decaf7ea --filename=mycore +If someone sends you a bug report or a comment via email and you wish to import +it into your issues list, you can use the 'am' command to do it's best to +import it. It will try and figure out if an issue is already mentioned and if +so, will try and import the email as a comment for that particular issue. In +the case where an existing issue is not found, it will import the email as a +new issue: + + $ cil am email.txt + Finally, because the cil issue files reside on the filesystem in flat files, there needs to be a way to check the integrity of the issues, therefore you can run this to do checks regarding the whole issue list: diff --git a/bin/cil b/bin/cil index 72d2389..8cbb57e 100755 --- a/bin/cil +++ b/bin/cil @@ -26,6 +26,9 @@ use File::Touch; use File::Glob ':glob'; use File::Basename; use File::Slurp qw(read_file write_file); +use Email::Simple; +use Email::Date qw(find_date); + use CIL; use CIL::Issue; use CIL::Comment; @@ -234,35 +237,7 @@ sub cmd_add { $issue->AssignedTo("$gan <$gae>"); $issue->Description("Description ..."); - my $edit = $y; - - # keep going until we get a valid issue or we want to quit - while ( $edit eq $y ) { - # read in the new issue text - my $fh = CIL::Utils->solicit( $issue->as_output ); - $issue = CIL::Issue->new_from_fh( 'tmp', $fh ); - - # check if the issue is valid - if ( $issue->is_valid($cil) ) { - $edit = 'n'; - } - else { - msg($_) foreach @{ $issue->errors }; - print 'Would you like to re-edit (y/n): '; - $edit = ; - chomp $edit; - print "\n"; - } - } - - # if the issue is still invalid, they quit without correcting it - return unless $issue->is_valid( $cil ); - - # we've got the issue, so let's name it - my $unique_str = $issue->Inserted . $issue->Summary . $issue->Description; - $issue->set_name( substr(md5_hex($unique_str), 0, 8) ); - $issue->save($cil); - display_issue($cil, $issue); + add_issue_loop($cil, undef, $issue); } sub cmd_edit { @@ -289,10 +264,7 @@ sub cmd_edit { } else { msg($_) foreach @{ $issue->errors }; - print 'Would you like to re-edit (y/n): '; - $edit = ; - chomp $edit; - print "\n"; + $edit = ans('Would you like to re-edit (y/n): '); } } @@ -320,41 +292,7 @@ sub cmd_comment { $comment->CreatedBy("$gan <$gae>"); $comment->Description("Description ..."); - my $edit = $y; - - # keep going until we get a valid issue or we want to quit - while ( $edit eq $y ) { - # read in the new comment text - my $fh = CIL::Utils->solicit( $comment->as_output ); - $comment = CIL::Comment->new_from_fh( 'tmp', $fh ); - - # check if the comment is valid - if ( $comment->is_valid($cil) ) { - $edit = 'n'; - } - else { - msg($_) foreach @{ $issue->errors }; - print 'Would you like to re-edit (y/n): '; - $edit = ; - chomp $edit; - print "\n"; - } - } - - # if the comment is still invalid, they quit without correcting it - return unless $comment->is_valid( $cil ); - - # we've got the comment, so let's name it - my $unique_str = $comment->Inserted . $issue->Description; - $comment->set_name( substr(md5_hex($unique_str), 0, 8) ); - - # finally, save it - $comment->save($cil); - - # add the comment to the issue, update it's timestamp and save it out - $issue->add_comment( $comment ); - $issue->save($cil); - display_issue_full($cil, $issue); + add_comment_loop($cil, undef, $issue, $comment); } sub cmd_attach { @@ -397,7 +335,7 @@ EOF $attachment->Size( $size ); # we've got the attachment, so let's name it - my $unique_str = $attachment->Inserted . $attachment->File; + my $unique_str = time . $attachment->Inserted . $attachment->File; $attachment->set_name( substr(md5_hex($unique_str), 0, 8) ); # finally, tell it who it's parent is and then save @@ -532,9 +470,187 @@ sub cmd_fsck { separator(); } +sub cmd_am { + my ($cil, $args, $email_filename) = @_; + + unless ( -f $email_filename ) { + fatal("couldn't load email '$email_filename'"); + } + + my $msg_text = read_file($email_filename); + + my $email = Email::Simple->new($msg_text); + unless ( defined $email ) { + fatal("email file '$email_filename' didn't look like an email"); + } + + # extract some fields + my $subject = $email->header('Subject'); + my $from = $email->header('From'); + my $date = find_date($email)->datetime; + my $body = $email->body; + + # see if we can find any issue names in either the subject or the body + my @issue_names; + foreach my $text ( $subject, $body ) { + my @new = ( $text =~ /\b\#?([0-9a-f]{8})\b/gxms ); + push @issue_names, @new; + } + + msg("Found possible issue names in email: ", ( join(' ', @issue_names) || '[none]' )); + + my %issue; + foreach ( @issue_names ) { + my $i = eval { CIL::Issue->new_from_name($cil, $_) }; + next unless defined $i; + + $issue{$i->name} = $i; + } + + if ( keys %issue ) { + msg( "Found actual issues: " . (join(' ', keys %issue)) ); + + # create the new comment + my $comment = CIL::Comment->new('tmpname'); + $comment->Issue( '...' ); + $comment->CreatedBy( $from ); + $comment->Inserted( $date ); + # $comment->Updated( $date ); + $comment->Description( $body ); + + # display + display_comment($cil, $comment); + + # found at least one issue, so this might be a comment + my $issue; + if ( keys %issue == 1 ) { + $issue = (values %issue)[0]; + } + else { + my $ans = ans('To which issue would you like to add this comment: '); + + # ToDo: decide whether we let them choose an arbitrary issue, for + # now quit unless they choose one from the list + return unless exists $issue{$ans}; + + # got a valid issue_name, so set the parent name + $issue = $issue{$ans}; + } + + # set the parent issue + $comment->Issue( $issue->name ); + + add_comment_loop($cil, undef, $issue, $comment); + } + else { + msg("Couldn't find reference to any issues in the email."); + + # no issue found so make up the issue first + my $issue = CIL::Issue->new('tmpname'); + $issue->Summary( $subject ); + $issue->Status( 'New' ); + $issue->CreatedBy( $from ); + $issue->AssignedTo( "$gan <$gae>" ); + $issue->Inserted( $date ); + $issue->Updated( $date ); + $issue->Description( $body ); + + # display + display_issue_full($cil, $issue); + + # then ask if the user would like to add it + msg("Couldn't find any likely issues, so this might be a new one."); + my $ans = ans('Would you like to add this message as an issue shown above (y/n): '); + return unless $ans eq 'y'; + + add_issue_loop($cil, undef, $issue); + } +} + ## ---------------------------------------------------------------------------- # helpers +sub ans { + my ($msg) = @_; + print $msg; + my $ans = ; + chomp $ans; + print "\n"; + return $ans; +} + +sub add_issue_loop { + my ($cil, undef, $issue) = @_; + + my $edit = $y; + + # keep going until we get a valid issue or we want to quit + while ( $edit eq $y ) { + # read in the new issue text + my $fh = CIL::Utils->solicit( $issue->as_output ); + $issue = CIL::Issue->new_from_fh( 'tmp', $fh ); + + # check if the issue is valid + if ( $issue->is_valid($cil) ) { + $edit = 'n'; + } + else { + msg($_) foreach @{ $issue->errors }; + $edit = ans('Would you like to re-edit (y/n): '); + } + } + + # if the issue is still invalid, they quit without correcting it + return unless $issue->is_valid( $cil ); + + # we've got the issue, so let's name it + my $unique_str = time . $issue->Inserted . $issue->Summary . $issue->Description; + $issue->set_name( substr(md5_hex($unique_str), 0, 8) ); + $issue->save($cil); + display_issue($cil, $issue); + + return $issue; +} + +sub add_comment_loop { + my ($cil, undef, $issue, $comment) = @_; + + my $edit = $y; + + # keep going until we get a valid issue or we want to quit + while ( $edit eq $y ) { + # read in the new comment text + my $fh = CIL::Utils->solicit( $comment->as_output ); + $comment = CIL::Comment->new_from_fh( 'tmp', $fh ); + + # check if the comment is valid + if ( $comment->is_valid($cil) ) { + $edit = 'n'; + } + else { + msg($_) foreach @{ $issue->errors }; + $edit = ans('Would you like to re-edit (y/n): '); + } + } + + # if the comment is still invalid, they quit without correcting it + return unless $comment->is_valid( $cil ); + + # we've got the comment, so let's name it + my $unique_str = time . $comment->Inserted . $issue->Description; + $comment->set_name( substr(md5_hex($unique_str), 0, 8) ); + + # finally, save it + $comment->save($cil); + + # add the comment to the issue, update it's timestamp and save it out + $issue->add_comment( $comment ); + $issue->save($cil); + display_issue_full($cil, $issue); + + return $comment; +} + sub check_paths { my ($cil) = @_; @@ -669,26 +785,38 @@ sub display_issue_full { my $comments = $cil->get_comments_for( $issue ); foreach my $comment ( @$comments ) { - title( 'Comment ' . $comment->name() ); - field( 'CreatedBy', $comment->CreatedBy() ); - field( 'Inserted', $comment->Inserted() ); - field( 'Updated', $comment->Inserted() ); - text('Description', $comment->Description()); + display_comment( $cil, $comment ); } my $attachments = $cil->get_attachments_for( $issue ); foreach my $attachment ( @$attachments ) { - title( 'Attachment ' . $attachment->name() ); - field( 'Filename', $attachment->Filename() ); - field( 'CreatedBy', $attachment->CreatedBy() ); - field( 'Inserted', $attachment->Inserted() ); - field( 'Updated', $attachment->Inserted() ); + display_attachment( $cil, $attachment ); msg(); } separator(); } +sub display_comment { + my ($cil, $comment) = @_; + + title( 'Comment ' . $comment->name() ); + field( 'CreatedBy', $comment->CreatedBy() ); + field( 'Inserted', $comment->Inserted() ); + field( 'Updated', $comment->Inserted() ); + text('Description', $comment->Description()); +} + +sub display_attachment { + my ($cil, $attachment) = @_; + + title( 'Attachment ' . $attachment->name() ); + field( 'Filename', $attachment->Filename() ); + field( 'CreatedBy', $attachment->CreatedBy() ); + field( 'Inserted', $attachment->Inserted() ); + field( 'Updated', $attachment->Inserted() ); +} + ## ---------------------------------------------------------------------------- # helper functions for this command line tool @@ -815,6 +943,8 @@ cil - the command-line issue list $ cil extract decaf7ea $ cil extract decaf7ea --filename=other_filename.txt + $ cil am email.txt + $ cil fsck =head1 DESCRIPTION @@ -874,6 +1004,14 @@ otherwise it will use the original one saved along with the attachment. Tries to help you organise your issues if any aren't valid or have broken relationships. +=item am + +Applies an email message to the issue list. It tries to figure out the type of +email it is, whether it is a new issue or a comment on an already existing +issue. For example, if it can find valid issue names in the subject or body of +the message, it adds it as a comment to that issue. If it can't find any valid +issue names, it presumes it's a new issue and adds that. + =back =head1 .cil diff --git a/debian-etch/control b/debian-etch/control index ded0c34..5348c53 100644 --- a/debian-etch/control +++ b/debian-etch/control @@ -11,7 +11,7 @@ Package: cil Section: perl Priority: optional Architecture: all -Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl +Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl, libemail-filter-perl Description: command line issue tracker 'cil' allows easy command-line creation of an issue tracker. It saves each issue locally and in plain text. Commands are given such that these issues can diff --git a/debian-lenny/control b/debian-lenny/control index ded0c34..5348c53 100644 --- a/debian-lenny/control +++ b/debian-lenny/control @@ -11,7 +11,7 @@ Package: cil Section: perl Priority: optional Architecture: all -Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl +Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl, libemail-filter-perl Description: command line issue tracker 'cil' allows easy command-line creation of an issue tracker. It saves each issue locally and in plain text. Commands are given such that these issues can diff --git a/issues/c_45cd5e23.cil b/issues/c_45cd5e23.cil new file mode 100644 index 0000000..7614c36 --- /dev/null +++ b/issues/c_45cd5e23.cil @@ -0,0 +1,23 @@ +Issue: 48eaec49 +CreatedBy: Francois Marier +Inserted: 2008-06-29T00:20:11 +Updated: 2008-06-29T12:05:50 + +On 2008-06-29 at 12:13:49, Andrew Chilton wrote: +> Any other thoughts about how it would work? Maybe that enough for now +> and we see what happens. + +Thinking about the use case where I'd want to add a comment on an issue +(like what I am doing now about the cil-am command), I think it would be +neat if cil-am could detect that. + +So for example, if you find a cil hash in the body of an email (or the +subject line), then it could be added as a comment. The timestamp of the +email determining the order. + +That would be a way to add a comment to an issue without depending on a web +interface. + +[snip] + +Francois diff --git a/issues/c_6f5bc459.cil b/issues/c_6f5bc459.cil new file mode 100644 index 0000000..b23a530 --- /dev/null +++ b/issues/c_6f5bc459.cil @@ -0,0 +1,18 @@ +Issue: 48eaec49 +CreatedBy: Andrew Chilton +Inserted: 2008-06-29T12:08:32 +Updated: 2008-06-29T12:11:14 + +Added ability to process an email message. It will either be added as a new +issue or as a comment to an existing issue. The process for determining what +happens is as follows: + +* a list of possible issue names is compiled from the subject and body of the + email +* of those, it checks if any are valid in the current list + +If any are valid, it will add the email as a comment to that one (if there is +only one) or will ask you which to add it to if there are more than one). + +In the case where there is no valid issues, it will add the email as a new +issue. diff --git a/issues/i_48eaec49.cil b/issues/i_48eaec49.cil index 1c7bb59..ad0a5b5 100644 --- a/issues/i_48eaec49.cil +++ b/issues/i_48eaec49.cil @@ -1,11 +1,13 @@ Summary: Add command 'am' -Status: New +Status: Finished CreatedBy: Andrew Chilton AssignedTo: Andrew Chilton Label: Milestone-v0.4 Label: Type-Enhancement +Comment: 45cd5e23 +Comment: 6f5bc459 Inserted: 2008-06-28T23:57:56 -Updated: 2008-06-29T00:07:15 +Updated: 2008-06-29T12:11:14 This command would take a mailbox and apply it as a new bug. It would take the first subject as the summary and the body as the issue decription. Then, each -- 2.39.5