#! /usr/bin/perl
-# Time Tracker
-#
-# Copyright (c) 2007 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
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
{
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;
}
}
}
{
my $min = shift;
- return 15 if $min < 15;
+ return 15 if $min > 0 && $min < 15;
return $min - $min%15 if ($min%15 < 3);
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);
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});
exit(0);
}
+sub delete_task
+{
+ my $query = q{DELETE FROM stempel WHERE stop IS NULL};
+ $dbh->do ($query);
+
+ 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) {
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_all
+sub list
{
- # my $query = q{SELECT customer,sum(time) FROM stempel GROUP BY customer ORDER BY customer};
- my $query = q{SELECT start,customer,time,task FROM stempel WHERE time IS NOT NULL ORDER BY customer,start};
+ my $month = shift;
+ my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
+ my $query = q{SELECT start,customer,time,task FROM stempel WHERE time IS NOT NULL };
+
+ if ($month =~ /^(\d{4})-?(\d\d?)$/) {
+ my $pivot = $1 . '-' . $2 . '-01';
+ $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
+ } elsif ($month =~ /^(\d{4})$/) {
+ my $pivot = $1 . '-01-01';
+ $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 year' ";
+ } elsif ($month =~ /^(\d\d?)$/) {
+ my $pivot = $d_year+1900 . '-' . $1 . '-01';
+ $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
+ } elsif ($month !~ /^all$/) {
+ my $pivot = sprintf('%04d-%02d-01', $d_year+1900, $d_mon+1);
+ $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
+ }
+ $query .= "ORDER BY customer,start";
my $sth = $dbh->prepare ($query);
sub toggle_task
{
if (is_open) {
- list_open;
+ list_open 1;
} else {
open_task;
}
}
+sub late_close_task
+{
+ my $delta = shift;
+ my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
+
+ return unless is_open;
+
+ my $query = sprintf("UPDATE stempel SET stop=start + interval '%d minutes',time=%d WHERE stop IS NULL",
+ $delta, quarter($delta));
+ $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 %options = ('list' => \&list_all,
+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;
-toggle_task;
+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;
+}