Move back task by n minutes via -b parameter
[infodrom.org/service.infodrom.org] / src / InfoCon / stempel / stempel
1 #! /usr/bin/perl
2
3 #  Time Tracker
4 #
5 #  Copyright (c) 2007,8,13  Martin Schulze <joey@infodrom.org>
6 #
7 #  This program is free software; you can redistribute it and/or modify
8 #  it under the terms of the GNU General Public License as published by
9 #  the Free Software Foundation; either version 2 of the License, or
10 #  (at your option) any later version.
11 #
12 #  This program is distributed in the hope that it will be useful,
13 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #  GNU General Public License for more details.
16 #
17 #  You should have received a copy of the GNU General Public License
18 #  along with this program; if not, write to the Free Software
19 #  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
20
21 use strict;
22 use warnings;
23
24 use DBI;
25 use Getopt::Long;
26 use Term::ReadLine;
27
28 my $table = "stempel";
29 my $engine = "dbi:Pg:dbname=infocon";
30
31 my $dbh = DBI->connect($engine);
32 die "Access to database denied!\n" unless $dbh;
33 $dbh->do("SET DateStyle = 'ISO'");
34
35 sub min2hour
36 {
37     my $minutes = shift;
38
39     return sprintf('%02d:%02d', $minutes/60, $minutes%60);
40 }
41
42 sub is_open
43 {
44     my $query = q{SELECT count(*) AS count FROM stempel WHERE stop IS NULL};
45     my $sth = $dbh->prepare ($query);
46     if ($sth && $sth->execute > 0) {
47         my $row = $sth->fetchrow_hashref;
48         return $row->{count} > 0 ? 1 : 0;
49     }
50     return 0;
51 }
52
53 sub customerlist
54 {
55     my @res;
56
57     my $query = q{SELECT DISTINCT customer FROM stempel WHERE start > now() - interval '1 year' ORDER BY customer};
58     my $sth = $dbh->prepare ($query);
59     if ($sth && $sth->execute > 0) {
60         while ((my $row = $sth->fetchrow_hashref)) {
61             push @res, $row->{customer};
62         }
63     }
64     return @res;
65 }
66
67 sub open_task
68 {
69     my $term = new Term::ReadLine '';
70     my @customers = customerlist;
71
72     $term->addhistory($_) foreach (@customers);
73
74     my $attribs = $term->Attribs;
75     $attribs->{completion_entry_function} = $attribs->{list_completion_function};
76     $attribs->{completion_word} = \@customers;
77
78     printf "[%s]\n", join (', ', @customers);
79
80     my $customer = $term->readline ('Kunde: ');
81
82     if (defined $customer && length($customer) > 0) {
83         $customer =~ s/\s*$//;
84         $attribs->{completion_word} = undef;
85         my $task = $term->readline ('Aufgabe: ');
86         if (length($task) > 0) {
87             my $query = q{INSERT INTO stempel (start,customer,task) VALUES('now()',?,?)};
88             my $sth = $dbh->prepare($query);
89             $sth->execute($customer, $task);
90         }
91     }
92 }
93
94 sub quarter
95 {
96     my $min = shift;
97
98     return 15 if $min < 15;
99
100     return $min - $min%15 if ($min%15 < 3);
101
102     return $min + 15-$min%15;
103 }
104
105 sub hdiff
106 {
107     my ($sh, $sm, $eh, $em) = @_;
108
109     if ($em >= $sm) {
110         return ($eh-$sh)*60 + $em-$sm;
111     } else {
112         return ($eh-$sh)*60 - ($sm-$em);
113     }
114 }
115
116 sub close_task
117 {
118     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
119
120     my $query = q{SELECT oid,start FROM stempel WHERE stop IS NULL};
121     my $sth = $dbh->prepare ($query);
122     if ($sth && $sth->execute > 0) {
123         while ((my $row = $sth->fetchrow_hashref)) {
124             my @arr = split(/ /, $row->{start});
125             my @d = split(/-/, $arr[0]);
126
127             if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
128                 printf "Task not started today, aborting.\n";
129                 next;
130             }
131
132             my @t = split(/:/, $arr[1]);
133             my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
134
135             $query = sprintf('UPDATE stempel SET stop=now(),time=%d WHERE oid = %d',
136                              $int, $row->{oid});
137             $dbh->do ($query);
138             printf "Wrote %s.\n", min2hour($int);
139         }
140     }
141     exit(0);
142 }
143
144 sub delete_task
145 {
146     my $query = q{DELETE FROM stempel WHERE stop IS NULL};
147     $dbh->do ($query);
148
149     exit(0);
150 }
151
152 sub list_open
153 {
154     my $exit = shift;
155     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
156     my $query = q{SELECT customer,start,task FROM stempel WHERE time IS NULL ORDER BY start,customer};
157
158     my $sth = $dbh->prepare ($query);
159     if ($sth && $sth->execute > 0) {
160         while ((my $row = $sth->fetchrow_hashref)) {
161             my @arr = split(/ /, $row->{start});
162             my $day = $arr[0];
163             my @d = split(/-/, $day);
164
165             if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
166                 printf "Task not started today.\n";
167                 next if $exit eq 1;
168             }
169
170             my @t = split(/:/, $arr[1]);
171             my $time = sprintf('%02d:%02d', $t[0], $t[1]);
172             my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
173
174             printf "%-15s  %s %s (%s)  %s\n", $row->{customer}, $day, $time, min2hour($int), $row->{task};
175         }
176     }
177     exit 0 if $exit;
178 }
179
180 sub list
181 {
182     my $month = shift;
183     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
184     my $query = q{SELECT start,customer,time,task FROM stempel WHERE time IS NOT NULL };
185     
186     if ($month =~ /^(\d{4})-?(\d\d?)$/) {
187         my $pivot = $1 . '-' . $2 . '-01';
188         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
189     } elsif ($month =~ /^(\d{4})$/) {
190         my $pivot = $1 . '-01-01';
191         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 year' ";
192     } elsif ($month =~ /^(\d\d?)$/) {
193         my $pivot = $d_year+1900 . '-' . $1 . '-01';
194         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
195     } elsif ($month !~ /^all$/) {
196         my $pivot = sprintf('%04d-%02d-01', $d_year+1900, $d_mon+1);
197         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
198     }
199     $query .= "ORDER BY customer,start";
200
201     my $sth = $dbh->prepare ($query);
202
203     if ($sth && $sth->execute > 0) {
204         while ((my $row = $sth->fetchrow_hashref)) {
205             if (defined $row->{time}) {
206                 my $day = (split(/ /, $row->{start}))[0];
207                 my $time = min2hour $row->{time};
208                 printf "%-15s  %s  %s  %s\n", $row->{customer}, $day, $time, $row->{task};
209             }
210         }
211     }
212     exit 0;
213 }
214
215 sub toggle_task
216 {
217     if (is_open) {
218         list_open 1;
219     } else {
220         open_task;
221     }
222 }
223
224 sub late_close_task
225 {
226     my $delta = shift;
227     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
228
229     return unless is_open;
230
231     my $query = sprintf("UPDATE stempel SET stop=start + interval '%d minutes',time=%d WHERE stop IS NULL",
232                         $delta, quarter($delta));
233     $dbh->do($query);
234 }
235
236 sub move_task
237 {
238     my $minutes = shift;
239
240     return unless is_open;
241
242     my $query = sprintf("UPDATE stempel SET start = start - interval '%d minutes' WHERE stop IS NULL", $minutes);
243     $dbh->do($query);
244 }
245
246 sub alter_task
247 {
248     return unless is_open;
249
250     list_open;
251
252     my $term = new Term::ReadLine '';
253
254     my $task = $term->readline ('Aufgabe: ');
255
256     if (length($task) > 0) {
257         my $query = q{UPDATE stempel SET task=? WHERE stop IS NULL};
258         my $sth = $dbh->prepare($query);
259         $sth->execute($task);
260     }
261     exit 0;
262 }
263
264 sub help
265 {
266     print <<"END";
267 stempel  Copyright (c) 2007,8  Martin Schulze <joey\@infodrom.org>
268     --list [month] list month [default=current|all]
269     --back n       move start of current task back by n minutes
270     --open         list open
271     --close time   close open task
272     --help         this text
273     --end|-d       terminate task
274     --delete       delete open task
275     --task         alter task content
276 END
277     exit;
278 }
279
280 my $opt_close = undef;
281 my $opt_list = undef;
282 my $opt_back = undef;
283 my %options = ('list:s' => \$opt_list,
284                'back=i' => \$opt_back,
285                'open' => \&list_open,
286                'help' => \&help,
287                'close=i' => \$opt_close,
288                'delete' => \&delete_task,
289                'task|t' => \&alter_task,
290                'terminate|end|d' => \&close_task,
291                );
292 GetOptions %options;
293
294 if ($opt_close) {
295     late_close_task $opt_close;
296 } elsif (defined $opt_list) {
297     list $opt_list;
298 } elsif (defined $opt_back) {
299     move_task $opt_back;
300 } else {
301     toggle_task;
302 }