Only display current customers
[infodrom.org/service.infodrom.org] / src / InfoCon / stempel / stempel
1 #! /usr/bin/perl
2
3 #  Time Tracker
4 #
5 #  Copyright (c) 2007,8  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 if (!$dbh) {
33     print "Access to database denied!\n";
34     return 1;
35 }
36 $dbh->do("SET DateStyle = 'ISO'");
37
38 sub min2hour
39 {
40     my $minutes = shift;
41
42     return sprintf('%02d:%02d', $minutes/60, $minutes%60);
43 }
44
45 sub is_open
46 {
47     my $query = q{SELECT count(*) AS count FROM stempel WHERE stop IS NULL};
48     my $sth = $dbh->prepare ($query);
49     if ($sth && $sth->execute > 0) {
50         my $row = $sth->fetchrow_hashref;
51         return $row->{count} > 0 ? 1 : 0;
52     }
53     return 0;
54 }
55
56 sub customerlist
57 {
58     my @res;
59
60     my $query = q{SELECT DISTINCT customer FROM stempel WHERE start > now() - interval '1 year' ORDER BY customer};
61     my $sth = $dbh->prepare ($query);
62     if ($sth && $sth->execute > 0) {
63         while ((my $row = $sth->fetchrow_hashref)) {
64             push @res, $row->{customer};
65         }
66     }
67     return @res;
68 }
69
70 sub open_task
71 {
72     my $term = new Term::ReadLine '';
73     my @customers = customerlist;
74
75     $term->addhistory($_) foreach (@customers);
76
77     my $attribs = $term->Attribs;
78     $attribs->{completion_entry_function} = $attribs->{list_completion_function};
79     $attribs->{completion_word} = \@customers;
80
81     printf "[%s]\n", join (', ', @customers);
82
83     my $customer = $term->readline ('Kunde: ');
84
85     if (defined $customer && length($customer) > 0) {
86         $customer =~ s/\s*$//;
87         $attribs->{completion_word} = undef;
88         my $task = $term->readline ('Aufgabe: ');
89         if (length($task) > 0) {
90             my $query = q{INSERT INTO stempel (start,customer,task) VALUES('now()',?,?)};
91             my $sth = $dbh->prepare($query);
92             $sth->execute($customer, $task);
93         }
94     }
95 }
96
97 sub quarter
98 {
99     my $min = shift;
100
101     return 15 if $min < 15;
102
103     return $min - $min%15 if ($min%15 < 3);
104
105     return $min + 15-$min%15;
106 }
107
108 sub hdiff
109 {
110     my ($sh, $sm, $eh, $em) = @_;
111
112     if ($em >= $sm) {
113         return ($eh-$sh)*60 + $em-$sm;
114     } else {
115         return ($eh-$sh)*60 - ($sm-$em);
116     }
117 }
118
119 sub close_task
120 {
121     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
122
123     my $query = q{SELECT oid,start FROM stempel WHERE stop IS NULL};
124     my $sth = $dbh->prepare ($query);
125     if ($sth && $sth->execute > 0) {
126         while ((my $row = $sth->fetchrow_hashref)) {
127             my @arr = split(/ /, $row->{start});
128             my @d = split(/-/, $arr[0]);
129
130             if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
131                 printf "Task not started today, aborting.\n";
132                 next;
133             }
134
135             my @t = split(/:/, $arr[1]);
136             my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
137
138             $query = sprintf('UPDATE stempel SET stop=now(),time=%d WHERE oid = %d',
139                              $int, $row->{oid});
140             $dbh->do ($query);
141             printf "Wrote %s.\n", min2hour($int);
142         }
143     }
144     exit(0);
145 }
146
147 sub delete_task
148 {
149     my $query = q{DELETE FROM stempel WHERE stop IS NULL};
150     $dbh->do ($query);
151
152     exit(0);
153 }
154
155 sub list_open
156 {
157     my $exit = shift;
158     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
159     my $query = q{SELECT customer,start,task FROM stempel WHERE time IS NULL ORDER BY start,customer};
160
161     my $sth = $dbh->prepare ($query);
162     if ($sth && $sth->execute > 0) {
163         while ((my $row = $sth->fetchrow_hashref)) {
164             my @arr = split(/ /, $row->{start});
165             my $day = $arr[0];
166             my @d = split(/-/, $day);
167
168             if ($d[0] != $d_year+1900 || $d[1] != $d_mon+1 || $d[2] != $d_mday) {
169                 printf "Task not started today.\n";
170                 next if $exit eq 1;
171             }
172
173             my @t = split(/:/, $arr[1]);
174             my $time = sprintf('%02d:%02d', $t[0], $t[1]);
175             my $int = quarter(hdiff($t[0], $t[1], $d_hour, $d_min));
176
177             printf "%-15s  %s %s (%s)  %s\n", $row->{customer}, $day, $time, min2hour($int), $row->{task};
178         }
179     }
180     exit 0 if $exit;
181 }
182
183 sub list
184 {
185     my $month = shift;
186     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
187     my $query = q{SELECT start,customer,time,task FROM stempel WHERE time IS NOT NULL };
188     
189     if ($month =~ /^(\d{4})-?(\d\d?)$/) {
190         my $pivot = $1 . '-' . $2 . '-01';
191         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
192     } elsif ($month =~ /^(\d{4})$/) {
193         my $pivot = $1 . '-01-01';
194         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 year' ";
195     } elsif ($month =~ /^(\d\d?)$/) {
196         my $pivot = $d_year+1900 . '-' . $1 . '-01';
197         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
198     } elsif ($month !~ /^all$/) {
199         my $pivot = sprintf('%04d-%02d-01', $d_year+1900, $d_mon+1);
200         $query .= "AND start >= '$pivot' AND start <= '$pivot'::date + interval '1 month' ";
201     }
202     $query .= "ORDER BY customer,start";
203
204     my $sth = $dbh->prepare ($query);
205
206     if ($sth && $sth->execute > 0) {
207         while ((my $row = $sth->fetchrow_hashref)) {
208             if (defined $row->{time}) {
209                 my $day = (split(/ /, $row->{start}))[0];
210                 my $time = min2hour $row->{time};
211                 printf "%-15s  %s  %s  %s\n", $row->{customer}, $day, $time, $row->{task};
212             }
213         }
214     }
215     exit 0;
216 }
217
218 sub toggle_task
219 {
220     if (is_open) {
221         list_open 1;
222     } else {
223         open_task;
224     }
225 }
226
227 sub late_close_task
228 {
229     my $delta = shift;
230     my ($d_sec,$d_min,$d_hour,$d_mday,$d_mon,$d_year,$d_wday,$d_isdst) = localtime();
231
232     return unless is_open;
233
234     my $query = sprintf("UPDATE stempel SET stop=start + interval '%d minutes',time=%d WHERE stop IS NULL",
235                         $delta, quarter($delta));
236     $dbh->do($query);
237 }
238
239 sub alter_task
240 {
241     return unless is_open;
242
243     list_open;
244
245     my $term = new Term::ReadLine '';
246
247     my $task = $term->readline ('Aufgabe: ');
248
249     if (length($task) > 0) {
250         my $query = q{UPDATE stempel SET task=? WHERE stop IS NULL};
251         my $sth = $dbh->prepare($query);
252         $sth->execute($task);
253     }
254     exit 0;
255 }
256
257 sub help
258 {
259     print <<"END";
260 stempel  Copyright (c) 2007,8  Martin Schulze <joey\@infodrom.org>
261     --list [month] list month [default=current|all]
262     --open         list open
263     --close time   close open task
264     --help         this text
265     --end|-d       terminate task
266     --delete       delete open task
267     --task         alter task content
268 END
269     exit;
270 }
271
272 my $opt_close = undef;
273 my $opt_list = undef;
274 my %options = ('list:s' => \$opt_list,
275                'open' => \&list_open,
276                'help' => \&help,
277                'close=i' => \$opt_close,
278                'delete' => \&delete_task,
279                'task|t' => \&alter_task,
280                'terminate|end|d' => \&close_task,
281                );
282 GetOptions %options;
283
284 if ($opt_close) {
285     late_close_task $opt_close;
286 } elsif (defined $opt_list) {
287     list $opt_list;
288 } else {
289     toggle_task;
290 }