[Neworleans-pm] Meeting Friday
Dave Cash
dave at gnofn.org
Wed Jun 8 07:52:37 PDT 2005
On Wed, 8 Jun 2005, Simon Dorfman wrote:
> I had an idea for the meeting. I'd like to publicly ask Dave Cash
> (oh, the pressure of asking in public ;-) if he'd be willing to
> share the perl script he uses to keep track of his hours for
> billing clients. If yes, maybe I'll start using it for keeping
> track of my hours. My current system is to manually enter start
> and stop times in excel. Ugh. Help is needed and maybe I'd learn
> a thing or two along the way. Would anyone else be interested in
> learning this? Whad'ya'say Dave? Wanna package up your script,
> put it online and give a little demo on how you use it?
Simon,
I'm happy to share my little script. And I'll be happy to explain
how it works. It's not too fancy, but I have been making little
modifications over the years and it does have a few nice features.
And some strange uses of Perl, especially in the way I implemented
(mostly for fun) format and write stuff. Code is below. I
apologize for the lack of docs and comments, but I never imagined
I'd publish it.
> Another thing we might work on is trying to come up with or find
> and configure an existing web-based calendar system for FairGrinds
> Coffeehouse. I was just looking at their calendar:
> http://www.fairgrinds.com/fgnews.html
Good idea, though I'm guessing the bests-of-show will be PHP apps.
> ...and it looks like they may have forgotten us. We're supposed
> to be meeting Friday, June 10th at 5:30pm - 8pm I don't see us on
> there and I even see two other things scheduled at times that
> conflict with ours.
>
> If I remember, I'll call tomorrow when they're open (948-3222) and
> ask them to make sure we're on the schedule.
Zoinks! Not again! I'll call them if you don't want to. Or I can
even drop by if necessary. Let me know how attempts at contact go.
Thanks!
Dave
/L\_/E\_/A\_/R\_/N\_/T\_/E\_/A\_/C\_/H\_/L\_/E\_/A\_/R\_/N\
Dave Cash Power to the People!
Frolicking in Fields of Garlic Right On-Line!
dave at gnofn.org Dig it all.
timeclock.pl:
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
use Time::ParseDate;
my $file_base = $ENV{ HOME } . '/.timeclock';
mkdir $file_base unless -e $file_base;
die "$file_base is not a directory$/" unless -d $file_base;
my ( $opt, $client ) = @ARGV;
if ( $opt !~ /^-/ ) {
$client = $opt;
$opt = '';
}
elsif ( $opt !~ /^-(calc|status|edit)$/ ) {
die "unknown option: $opt";
}
die "usage: clock.pl [-calc | -status | -edit] client\n" if ! $client;
my ( $client_name ) = $client =~ m#([^.]+)\.[^.]+$#;
$client_name ||= $client;
$client =~ s/\.clk$//;
my $client_file = $client =~ m#/# ? "$client.clk" : "$file_base/$client.clk";
my %codes;
if ( -f "$file_base/$client_name.codes" ) {
open CODES, "< $file_base/$client_name.codes";
while ( <CODES> ) {
chomp;
my ( $code, $description ) = m/^(\S+)\s+(.*)$/;
$codes{ $code } = $description;
}
close CODES;
}
if ( $opt eq '-calc' ) {
print calc( $client_file ), $/;
}
elsif ( $opt eq '-status' ) {
print get_status( $client_file ), ".$/";
}
elsif ( $opt eq '-edit' ) {
edit( $client_file );
print calc( $client_file ), $/;
}
else {
my $status = get_status( $client_file );
open (LOG, ">> $client_file") || die "$client_file: $!";
if ( $status eq 'stopped' ) {
print "starting work.\n";
print LOG 'start: ' . POSIX::strftime('%A %d %B %Y %H:%M:%S', localtime(time)) . $/;
close LOG;
}
else {
print "stopping work.\n";
print LOG 'stop: ' . POSIX::strftime('%A %d %B %Y %H:%M:%S', localtime(time)) . $/;
if ( %codes ) {
my $code = get_code( %codes ) if %codes;
print LOG 'code: ', $code, $/;
}
close LOG;
print 'Add a comment? [Y/n] ';
my $answer = <STDIN>;
chomp $answer;
edit( $client_file ) if (! $answer) || $answer =~ /^y/i;
print calc( $client_file ), $/;
}
}
sub calc {
my ( $client_file ) = @_;
$client_file =~ s/\.clk$// unless -e $client_file;
open ( CLIENT, "< $client_file" ) || die "$client_file: $!";
my $total_seconds = 0;
my %subtotals;
my $hold;
my $span;
while ( <CLIENT> ) {
chomp;
next unless /^(start|stop|code):\s/;
if ( $1 eq 'code' ) {
my ( $coding ) = m/^code:\s+(.*)$/;
$subtotals{ $coding } += $span;
}
else {
my ( $time ) = m/[tp]:\s+(.*)$/;
$time =~ s/^(...)\S+\s+(\d+)\s+(...)\S*\s+(\d+)\s+([0-9:]+)/$1 $3 $2 $5 $4/;
my $seconds = parsedate($time, NO_RELATIVE => 1);
if ( /^start:/ ) {
$hold = $seconds;
}
elsif ( /^stop:/ ) {
$span = $seconds - $hold;
$total_seconds += $span;
$hold = undef;
}
}
}
close CLIENT;
my $report;
my $bas_fmt = '@>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>> @>>>>>>>>> @>>>>' . $/;
my $tab_fmt = ' -------- ----------' . $/;
my $ext_fmt = '~ ^<<<<<<<<<<<<<<<<<<<<<<<<' . $/;
$report .= scalar keys %subtotals ?
"Coded Subtotals for $client_name ($client_file):$/" :
"Total for $client_name ($client_file):$/";
foreach my $code ( sort keys %subtotals ) {
my $hours = int( $subtotals{ $code } / 3600 );
my $minutes = sprintf '%02d', int( ( $subtotals{ $code } % 3600 ) / 60 );
my $seconds = sprintf '%02d', $subtotals{ $code } % 60;
my $percentage = int( ( ( $subtotals{ $code } / $total_seconds ) * 100 ) + 0.5 );
my $dollars = sprintf( '%0.02f', int( ( $subtotals{ $code } / 3600 ) * 4 + 0.5 ) / .04 );
1 while $dollars =~ s/^(-?\d+)(\d{3})/$1,$2/;
my $time = "$hours:$minutes:$seconds";
formline $bas_fmt, $code, $codes{ $code }, $time, '$' . $dollars, $percentage . '%';
formline $ext_fmt, $codes{ $code } while $codes{ $code };
}
my $hours = int( $total_seconds / 3600 );
my $minutes = sprintf '%02d', int( ( $total_seconds % 3600 ) / 60 );
my $seconds = sprintf '%02d', $total_seconds % 60;
my $dollars = int( ( $total_seconds / 3600 ) * 4 + 0.5 ) / .04;
my $total_time = "$hours:$minutes:$seconds";
my $total_dollars = sprintf( '%0.02f', int( ( $total_seconds / 3600 ) * 4 + 0.5 ) / .04 );
1 while $total_dollars =~ s/^(-?\d+)(\d{3})/$1,$2/;
formline $tab_fmt if $^A;
my $nothing = '';
formline $bas_fmt, $nothing, $nothing, $total_time, '$' . $total_dollars, $nothing;
$report .= $^A;
chomp $report;
return $report;
}
sub edit {
my ( $client_file ) = @_;
die "$client_file: $!" unless stat $client_file;
my $lines = int( `wc $client_file` ) - 3;
my $command_args = $lines ? "+$lines $client_file" : $client_file;
system "vi $command_args";
}
sub get_code {
my ( %codes ) = @_;
my $choice = 97;
my %choice_map;
foreach my $code ( sort keys %codes ) {
printf "%s. %s (%s)$/", chr( $choice ), $code, $codes{ $code };
$choice_map{ chr( $choice ) } = $code;
$choice ++;
}
print "Choose a code: ";
my $chosen_code = <STDIN>;
$chosen_code = lc( substr $chosen_code, 0, 1 );
print "CHOSEN CODE: '$chosen_code'", $/;
return exists $choice_map{ $chosen_code } ? $choice_map{ $chosen_code } : get_code( %codes );
}
sub get_status {
my ( $client_file ) = @_;
system "touch $client_file" if ! -e $client_file;
open ( CLIENT, qq!cat $client_file | grep "^st[ao][rp]" | tail -1 |! ) || die "$client_file: $!";
my $last_line = <CLIENT> || '';
close CLIENT;
return $last_line =~ /^stop:/ || ! $last_line ? 'stopped' : 'started';
}
More information about the NewOrleans-pm
mailing list