Allow closing tasks not started today with -D
[infodrom.org/service.infodrom.org] / src / InfoCon / stempel / stempel
index 9c66cdb..7bb7bbc 100755 (executable)
@@ -1,8 +1,7 @@
 #! /usr/bin/perl
 
-#  Time Tracker
-#
-#  Copyright (c) 2007,8  Martin Schulze <joey@infodrom.org>
+#  InfoCon Time Tracker
+#  Copyright (c) 2007,8,13,14,15  Martin Schulze <joey@infodrom.org>
 #
 #  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
@@ -29,10 +28,7 @@ my $table = "stempel";
 my $engine = "dbi:Pg:dbname=infocon";
 
 my $dbh = DBI->connect($engine);
-if (!$dbh) {
-    print "Access to database denied!\n";
-    return 1;
-}
+die "Access to database denied!\n" unless $dbh;
 $dbh->do("SET DateStyle = 'ISO'");
 
 sub min2hour
@@ -57,39 +53,89 @@ sub customerlist
 {
     my @res;
 
-    my $query = q{SELECT DISTINCT customer FROM stempel ORDER BY customer};
+    my $query = q{SELECT DISTINCT customer FROM stempel WHERE start > now() - interval '1 year' ORDER BY customer};
     my $sth = $dbh->prepare ($query);
     if ($sth && $sth->execute > 0) {
        while ((my $row = $sth->fetchrow_hashref)) {
            push @res, $row->{customer};
        }
     }
-    return @res;
+    return \@res;
+}
+
+sub tasklist
+{
+    my $customer = shift;
+    my @res;
+
+    my $query = q{SELECT DISTINCT task FROM stempel WHERE start > now() - interval '1 month' AND customer = ? ORDER BY task};
+    my $sth = $dbh->prepare($query);
+    if ($sth && $sth->execute($customer) > 0) {
+       while ((my $row = $sth->fetchrow_hashref)) {
+           push @res, $row->{task};
+       }
+    }
+    return \@res;
+}
+
+sub complete_customer
+{
+    my ($customer, $text, $line, $start) = @_;
+
+    # return () unless exists $answers->{category} && length $answers->{category};
+
+    my $sql = sprintf("SELECT DISTINCT task FROM stempel WHERE start > now() - interval '1 month' AND customer = '%s' AND task LIKE '%s%%' ORDER BY task",
+                     $customer,
+                     $line);
+
+    my $sth = $dbh->prepare($sql);
+    $sth->execute;
+    my @complete;
+    while (my $row = $sth->fetchrow_hashref) {
+       $row->{task} = substr $row->{task}, $start if $start;
+       push @complete, $row->{task};
+    }
+
+    return @complete;
 }
 
 sub open_task
 {
     my $term = new Term::ReadLine '';
-    my @customers = customerlist;
-
-    $term->addhistory($_) foreach (@customers);
+    my $customers = customerlist;
 
     my $attribs = $term->Attribs;
     $attribs->{completion_entry_function} = $attribs->{list_completion_function};
-    $attribs->{completion_word} = \@customers;
+    $attribs->{completion_word} = $customers;
 
-    printf "[%s]\n", join (', ', @customers);
+    printf "[%s]\n", join (', ', @$customers);
 
     my $customer = $term->readline ('Kunde: ');
 
     if (defined $customer && length($customer) > 0) {
        $customer =~ s/\s*$//;
-       $attribs->{completion_word} = undef;
-       my $task = $term->readline ('Aufgabe: ');
-       if (length($task) > 0) {
+
+       my $tasks = tasklist $customer;
+
+       $term->addhistory($_) foreach (@$tasks);
+       $attribs->{completion_word} = $tasks;
+       $attribs->{completion_entry_function} = undef;
+       $term->{completion_function} = sub {return complete_customer $customer, @_};
+       while (1) {
+           my $task = $term->readline ('Aufgabe: ');
+           return unless length $task;
+           return if $task eq 'q';
+
+           if ($task eq '?') {
+               printf "  %s\n", $_ foreach (@$tasks);
+               next;
+           }
+
+           $task =~ s/\s*$//;
            my $query = q{INSERT INTO stempel (start,customer,task) VALUES('now()',?,?)};
            my $sth = $dbh->prepare($query);
            $sth->execute($customer, $task);
+           last;
        }
     }
 }
@@ -98,7 +144,7 @@ sub quarter
 {
     my $min = shift;
 
-    return 15 if $min < 15;
+    return 15 if $min > 0 && $min < 15;
 
     return $min - $min%15 if ($min%15 < 3);
 
@@ -118,7 +164,9 @@ sub hdiff
 
 sub close_task
 {
+    my $really = shift;
     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
+    my $today = 1;
 
     my $query = q{SELECT oid,start FROM stempel WHERE stop IS NULL};
     my $sth = $dbh->prepare ($query);
@@ -128,12 +176,18 @@ sub close_task
            my @d = split(/-/, $arr[0]);
 
            if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
-               printf "Task not started today, aborting.\n";
-               next;
+               if ($really) {
+                   printf "Task not started today.\n";
+                   $today = 0;
+               } else {
+                   printf "Task not started today, aborting, use -D if this is intentional.\n";
+                   next;
+               }
            }
 
            my @t = split(/:/, $arr[1]);
            my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
+           $int += 24 * 60 unless $today;
 
            $query = sprintf('UPDATE stempel SET stop=now(),time=%d WHERE oid = %d',
                             $int, $row->{oid});
@@ -152,10 +206,25 @@ sub delete_task
     exit(0);
 }
 
+sub reopen_task
+{
+    my $sql = q{SELECT max(id) AS max_id FROM stempel};
+    my $sth = $dbh->prepare($sql);
+    if ($sth && $sth->execute > 0) {
+       my $row = $sth->fetchrow_hashref;
+       $sql = sprintf("UPDATE stempel SET stop = NULL, time = NULL WHERE id = %d", $row->{max_id});
+       $dbh->do($sql);
+    }
+
+    exit(0);
+}
+
 sub list_open
 {
+    my $exit = shift;
     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
     my $query = q{SELECT customer,start,task FROM stempel WHERE time IS NULL ORDER BY start,customer};
+    my $today = 1;
 
     my $sth = $dbh->prepare ($query);
     if ($sth && $sth->execute > 0) {
@@ -165,18 +234,20 @@ sub list_open
            my @d = split(/-/, $day);
 
            if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
-               printf "Task not started today, aborting.\n";
-               next;
+               printf "Task not started today.\n";
+               $today = 0;
+               next if defined $exit && $exit eq 1;
            }
 
            my @t = split(/:/, $arr[1]);
            my $time = sprintf('%02d:%02d', $t[0], $t[1]);
            my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
+           $int += 24 * 60 unless $today;
 
            printf "%-15s  %s %s (%s)  %s\n", $row->{customer}, $day, $time, min2hour($int), $row->{task};
        }
     }
-    exit 0;
+    exit 0 if $exit;
 }
 
 sub list
@@ -217,7 +288,7 @@ sub list
 sub toggle_task
 {
     if (is_open) {
-       list_open;
+       list_open 1;
     } else {
        open_task;
     }
@@ -235,28 +306,77 @@ sub late_close_task
     $dbh->do($query);
 }
 
+sub move_task
+{
+    my $minutes = shift;
+
+    return unless is_open;
+
+    my $query = sprintf("UPDATE stempel SET start = start - interval '%d minutes' WHERE stop IS NULL", $minutes);
+    $dbh->do($query);
+}
+
+sub alter_task
+{
+    return unless is_open;
+
+    list_open;
+
+    my $term = new Term::ReadLine '';
+
+    my $sql = q{SELECT customer,task FROM stempel WHERE stop IS NULL};
+    my $sth = $dbh->prepare($sql);
+    $sth->execute;
+    my $row = $sth->fetchrow_hashref;
+
+    my $tasks = tasklist $row->{'customer'};
+    $term->addhistory($_) foreach (@$tasks);
+
+    my $attribs = $term->Attribs;
+    $attribs->{completion_word} = $tasks;
+    $term->{completion_function} = sub {return complete_customer $row->{'customer'}, @_};
+
+    my $task = $term->readline ('Aufgabe: ');
+
+    if (length($task) > 0) {
+       my $query = q{UPDATE stempel SET task=? WHERE stop IS NULL};
+       my $sth = $dbh->prepare($query);
+       $sth->execute($task);
+    }
+    exit 0;
+}
+
 sub help
 {
     print <<"END";
 stempel  Copyright (c) 2007,8  Martin Schulze <joey\@infodrom.org>
     --list [month] list month [default=current|all]
+    --back n       move start of current task back by n minutes
     --open         list open
     --close time   close open task
     --help         this text
     --end|-d       terminate task
+    -D             terminate task not started today
     --delete       delete open task
+    --reopen       re-open last task
+    --task         alter task content
 END
     exit;
 }
 
 my $opt_close = undef;
 my $opt_list = undef;
+my $opt_back = undef;
 my %options = ('list:s' => \$opt_list,
+              'back=i' => \$opt_back,
               'open' => \&list_open,
               'help' => \&help,
               'close=i' => \$opt_close,
               'delete' => \&delete_task,
+              'reopen' => \&reopen_task,
+              'task|t' => \&alter_task,
               'terminate|end|d' => \&close_task,
+              'D' => sub {close_task(1);},
               );
 GetOptions %options;
 
@@ -264,6 +384,8 @@ if ($opt_close) {
     late_close_task $opt_close;
 } elsif (defined $opt_list) {
     list $opt_list;
+} elsif (defined $opt_back) {
+    move_task $opt_back;
 } else {
     toggle_task;
 }