Map Reduce Using Perl
View more presentations from Phil Whelan
package RemoteControl;
use Moose;
use Command;
has 'onCommands' => (
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
);
has 'offCommands' => (
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
);
sub BUILD {
my $self = shift;
for (my $i = 0; $i < 7; $i++) {
push @{$self->onCommands}, NoCommand->new;
push @{$self->offCommands}, NoCommand->new;
}
}
package RemoteControl;
use Moose;
use Command;
has 'onCommands' => (
traits => ['Array'],
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
handles => {
all_onCommands => 'elements',
add_onCommand => 'push',
map_onCommands => 'map',
filter_onCommands => 'grep',
find_onCommand => 'first',
get_onCommand => 'get',
join_onCommands => 'join',
count_onCommands => 'count',
has_onCommands => 'count',
has_no_onCommands => 'is_empty',
sorted_onCommands => 'sort',
},
);
has 'offCommands' => (
traits => ['Array'],
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
handles => {
all_offCommands => 'elements',
add_offCommand => 'push',
map_offCommands => 'map',
filter_offCommands => 'grep',
find_offCommand => 'first',
get_offCommand => 'get',
join_offCommands => 'join',
count_offCommands => 'count',
has_offCommands => 'count',
has_no_offCommands => 'is_empty',
sorted_offCommands => 'sort',
},
);
sub BUILD {
my $self = shift;
for (my $i = 0; $i < 7; $i++) {
$self->add_onCommand(NoCommand->new);
$self->add_offCommand(NoCommand->new);
}
}
print "--[ Receiver: Ceiling Fan ]--------------------------------------------";
my $myCeilingFan = CeilingFan->new;
isa_ok($myCeilingFan, 'CeilingFan');
$myCeilingFan->low;
is($myCeilingFan->state, 'low',
q{.. Set ceiling fan ON LOW});
$myCeilingFan->medium;
is($myCeilingFan->state, 'medium',
q{.. Set ceiling fan ON MED});
$myCeilingFan->high;
is($myCeilingFan->state, 'high',
q{.. Set ceiling fan ON HIGH});
$myCeilingFan->off;
is($myCeilingFan->state, 'off',
q{.. Set ceiling fan OFF});
print "--[ Receiver: Hot Tub ]------------------------------------------------";
my $myHotTub = HotTub->new;
isa_ok($myHotTub, 'HotTub');
$myHotTub->on;
is($myHotTub->state, 1,
q{.. Hot tub is on});
$myHotTub->off;
is($myHotTub->state, 0,
q{.. Hot tub is off});
$myHotTub->on;
$myHotTub->circulate;
$myHotTub->temperature(50);
is($myHotTub->temperature, 50,
q{.. Hot tub temperature is set to 50 degrees});
$myHotTub->temperature(150);
is($myHotTub->temperature, 150,
q{.. Hot tub temperature is set to 150 degrees});
$myHotTub->temperature(90);
is($myHotTub->temperature, 90,
q{.. Hot tub temperature is set to 90 degrees});
print "--[ Command: Ceiling Fan Hi / Lo / Med / Off]--------------------------";
my $myTestCeilingFan = CeilingFan->new;
print "-----------------------------------------------------------< ON HI >---";
$myTestCeilingFan->medium; # fan set randomly at medium
is($myTestCeilingFan->state, 'medium',
q{Fan is ON MED at start.});
my $myCeilingFanHighCommand =
CeilingFanHighCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanHighCommand, 'CeilingFanHighCommand');
$myCeilingFanHighCommand->execute;
is($myCeilingFanHighCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via Command.});
$myCeilingFanHighCommand->undo;
is($myCeilingFanHighCommand->ceilingFan->state, 'medium',
q{Fan (re)set ON MED via undo.});
print "----------------------------------------------------------< ON MED >---";
$myTestCeilingFan->low; # fan set randomly at low
is($myTestCeilingFan->state, 'low',
q{Fan is ON LO at start.});
my $myCeilingFanMediumCommand =
CeilingFanMediumCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanMediumCommand, 'CeilingFanMediumCommand');
$myCeilingFanMediumCommand->execute;
is($myCeilingFanMediumCommand->ceilingFan->state, 'medium',
q{Fan (re)set ON MED via Command.});
$myCeilingFanMediumCommand->undo;
is($myCeilingFanMediumCommand->ceilingFan->state, 'low',
q{Fan (re)set ON LO via undo.});
print "----------------------------------------------------------< ON LO >---";
$myTestCeilingFan->high; # fan set randomly at high
is($myTestCeilingFan->state, 'high',
q{Fan is ON HI at start.});
my $myCeilingFanLowCommand =
CeilingFanLowCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanLowCommand, 'CeilingFanLowCommand');
$myCeilingFanLowCommand->execute;
is($myCeilingFanLowCommand->ceilingFan->state, 'low',
q{Fan (re)set ON LO via Command.});
$myCeilingFanLowCommand->undo;
is($myCeilingFanLowCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via undo.});
print "-------------------------------------------------------------< OFF >---";
$myTestCeilingFan->high; # fan set randomly at high
is($myTestCeilingFan->state, 'high',
q{Fan is ON HI at start.});
my $myCeilingFanOffCommand =
CeilingFanOffCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanOffCommand, 'CeilingFanOffCommand');
$myCeilingFanOffCommand->execute;
is($myCeilingFanLowCommand->ceilingFan->state, 'off',
q{Fan (re)set OFF via Command.});
$myCeilingFanOffCommand->undo;
is($myCeilingFanLowCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via undo.});
package Command;
use Moose;
use HouseholdDevices;
sub execute {};
sub undo {};
package LightOnCommand;
use Moose;
extends 'Command';
has 'light' => (
is => 'rw',
isa => 'Light',
);
sub execute {
print q{< Command id='LightOnCommand' >};
my $self = shift;
$self->light->on;
print q{< /Command >};
}
sub undo {
print q{< Command id='LightOnCommand' >};
my $self = shift;
$self->light->off;
print q{< /Command >};
}
package LightOffCommand;
use Moose;
extends 'Command';
has 'light' => (
is => 'rw',
isa => 'Light',
);
sub execute {
print q{< Command id='LightOffCommand >};
my $self = shift;
$self->light->off;
print q{< /Command >};
}
sub undo {
print q{< Command id='LightOffCommand >};
my $self = shift;
$self->light->on;
print q{< /Command >};
}
package LivingRoomLightOnCommand;
use Moose;
extends 'Command';
has 'light' => (
is => 'rw',
isa => 'Light',
);
sub execute {
print q{< Command id='LivingRoomLightOnCommand' >};
my $self = shift;
$self->light->on;
print q{< /Command >};
}
sub undo {
print q{< Command id='LivingRoomLightOnCommand' >};
my $self = shift;
$self->light->off;
print q{< /Command >};
}
package LivingRoomLightOffCommand;
use Moose;
extends 'Command';
has 'light' => (
is => 'rw',
isa => 'Light',
);
sub execute {
print q{< Command id='LivingRoomLightOffCommand >};
my $self = shift;
$self->light->off;
print q{< /Command >};
}
sub undo {
print q{< Command id='LivingRoomLightOffCommand >};
my $self = shift;
$self->light->on;
print q{< /Command >};
}
package CeilingFanHighCommand;
use Moose;
use Moose::Util::TypeConstraints;
extends 'Command';
has 'ceilingFan' => (
is => 'rw',
isa => 'CeilingFan',
);
enum 'fanState' => qw(high medium low off);
has 'backState' => (
is => 'rw',
isa => 'fanState',
default => 'off',
);
sub execute {
print q{}; };
my $self = shift;
$self->backState($self->ceilingFan->state);
$self->ceilingFan->high;
print q{
}
sub undo {
print q{}; };
my $self = shift;
$self->ceilingFan->low if ($self->backState eq 'low');
$self->ceilingFan->medium if ($self->backState eq 'medium');
$self->ceilingFan->off if ($self->backState eq 'off');
print q{
}
package CeilingFanOffCommand;
use Moose;
use Moose::Util::TypeConstraints;
extends 'Command';
has 'ceilingFan' => (
is => 'rw',
isa => 'CeilingFan',
);
enum 'fanState' => qw(high medium low off);
has 'backState' => (
is => 'rw',
isa => 'fanState',
default => 'off',
);
sub execute {
print q{}; };
my $self = shift;
$self->backState($self->ceilingFan->state);
$self->ceilingFan->off;
print q{
}
sub undo {
print q{}; };
my $self = shift;
$self->ceilingFan->high if ($self->backState eq 'high');
$self->ceilingFan->medium if ($self->backState eq 'medium');
$self->ceilingFan->low if ($self->backState eq 'low');
print q{
}
package Fanable;
use Moose::Role;
use Moose::Util::TypeConstraints;
requires 'execute';
has 'ceilingFan' => (
is => 'rw',
isa => 'CeilingFan',
);
enum 'fanState' => qw(high medium low off);
has 'backState' => (
is => 'rw',
isa => 'fanState',
default => 'off',
);
before 'execute' => sub {
my $self = shift;
$self->backState($self->ceilingFan->state);
};
sub undo {
print q{}; };
my $self = shift;
$self->ceilingFan->high if ($self->backState eq 'high');
$self->ceilingFan->medium if ($self->backState eq 'medium');
$self->ceilingFan->low if ($self->backState eq 'low');
$self->ceilingFan->off if ($self->backState eq 'off');
print q{
}
package CeilingFanHighCommand;
use Moose;
with 'Fanable';
extends 'Command';
sub execute {
print q{}; };
my $self = shift;
$self->ceilingFan->high;
print q{
};
package CeilingFanOffCommand;
use Moose;
with 'Fanable';
extends 'Command';
sub execute {
print q{}; };
my $self = shift;
$self->ceilingFan->off;
print q{
};

package HotTubOnCommand;
use Moose;
extends 'Command';
has 'hotTub' => (
is => 'rw',
isa => 'HotTub',
);
sub execute {
print q{}; };
my $self = shift;
$self->hotTub->on;
$self->hotTub->temperature(87);
$self->hotTub->circulate;
print q{
}
sub undo {
print q{}; };
my $self = shift;
$self->hotTub->off;
print q{
}
package HotTubOffCommand;
use Moose;
extends 'Command';
has 'hotTub' => (
is => 'rw',
isa => 'HotTub',
);
sub execute {
print q{}; };
my $self = shift;
$self->hotTub->temperature(35);
$self->hotTub->off;
print q{
}
sub undo {
print q{}; };
my $self = shift;
$self->hotTub->on;
print q{
}
package NoCommand;
use Moose;
extends 'Command';
sub execute {
print q{}; };
print q{
}
sub undo {
print q{}; };
print q{
}
package MacroCommand;
use Moose;
extends 'Command';
has 'Commands' => (
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
);
sub BUILD {
my $self = shift;
push @{$self->Commands}, NoCommand->new;
}
sub execute {
my $self = shift;
print q{}; };
foreach my $thisCommand (@{$self->Commands}) {
$thisCommand->execute;
}
print q{
}
sub undo {
my $self = shift;
print q{}; };
foreach my $thisCommand (@{$self->Commands}) {
$thisCommand->undo;
}
print q{
}
package HouseholdDevices;
use Moose;
package Light;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'lightScale'
=> as Num
=> where {
($_ >= 0) && ($_ <= 100);
};
has 'level' => (
is => 'rw',
isa => 'lightScale',
reader => 'getLevel',
writer => 'setLevel',
default => 0,
);
sub on {
my $self = shift;
$self->setLevel(100);
print q{Light switched on.};
};
sub off {
my $self = shift;
$self->setLevel(0);
print q{Light switched off.};
};
sub dim {
my ($self, $level) = @_;
$self->setLevel($level);
print q{Light dimmed to }.$self->getLevel.q{%};
};
package BangBangLight;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'boolScale'
=> as Num
=> where {
($_ == 0) || ($_ == 1);
};
has 'state' => (
is => 'rw',
isa => 'boolScale',
default => 0,
);
sub on {
my $self = shift;
$self->state(1);
print q{Bang-bang light switched on.};
};
sub off {
my $self = shift;
$self->state(0);
print q{Bang-bang light switched off.};
};
package GardenLight;
use Moose;
use Moose::Util::TypeConstraints;
extends 'BangBangLight';
subtype 'lightTime'
=> as Str
=> where { /^\d+:\d\d\s+[PA]M$/i };
has 'duskTime' => (
is => 'rw',
isa => 'lightTime',
default => '8:30 PM',
);
has 'dawnTime' => (
is => 'rw',
isa => 'lightTime',
default => '6:30 AM',
);
before 'on', 'off' => sub {
print "Garden>";
};
after 'on', 'off' => sub {
print "< nedraG";
};
sub manualOn {
my $self = shift;
$self->on;
print q{Garden light auto on / off at }
. $self->duskTime . ' / ' . $self->dawnTime;
};
sub manualOff {
my $self = shift;
$self->off;
print q{Garden light auto on / off at }
. $self->duskTime . ' / ' . $self->dawnTime;
};
package CeilingFan;
use Moose;
use Moose::Util::TypeConstraints;
enum 'fanState' => qw(high medium low off);
has 'state' => (
is => 'rw',
isa => 'fanState',
default => 'off',
);
sub low {
my $self = shift;
$self->state('low');
print q{Ceiling fan is on [} . $self->state . q{].} ;
};
sub medium {
my $self = shift;
$self->state('medium');
print q{Ceiling fan is on [} . $self->state . q{].} ;
};
sub high {
my $self = shift;
$self->state('high');
print q{Ceiling fan is on [} . $self->state . q{].} ;
};
sub off {
my $self = shift;
$self->state('off');
print q{Ceiling fan is off.};
};
package HotTub;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'tubState'
=> as Num
=> where {
($_ == 0) || ($_ == 1);
};
has 'state' => (
is => 'rw',
isa => 'tubState',
default => 0,
);
subtype 'tubTemperature'
=> as Num
=> where {
($_ >= 0) && ($_ <= 250);
};
has 'temperature' => (
is => 'rw',
isa => 'tubTemperature',
default => 0,
);
sub on {
my $self = shift;
$self->state(1);
print q{Hot tub is on.};
};
sub off {
my $self = shift;
$self->state(0);
print q{Hot tub is off.};
};
sub circulate {
my $self = shift;
print q{Hot tub is bubbling} if $self->state;
};
sub jetsOn {
my $self = shift;
print q{Jets are on} if $self->state;
};
sub jetsOff {
my $self = shift;
print q{Jets are off} if $self->state;
};
around 'temperature' => sub {
my $originalMethod = shift;
my $self = shift;
my $newValue = shift;
my $oldValue = $self->$originalMethod;
# Note: $self->temperature would lead to deep recursion
return $oldValue unless $newValue;
print "Steaming it up to $newValue degrees"
if ($newValue > $oldValue);
print "Cooling it down to $newValue degrees"
unless ($newValue > $oldValue);
$self->$originalMethod($newValue);
print "Temperature is now: " . $self->$originalMethod;
# safe to call, no deep recursion
};
package Stereo;
use Moose;
use Moose::Util::TypeConstraints;
enum 'stereoState' => qw(CD DVD Radio);
has 'state' => (
is => 'rw',
isa => 'stereoState',
default => 'Radio',
);
subtype 'stereoScale'
=> as Num
=> where {
($_ >= 0) && ($_ <= 11);
};
has 'volume' => (
is => 'rw',
isa => 'stereoScale',
default => 7,
trigger => sub {print q{Volume reset to } . $_[0]->volume},
);
sub on {
my $self = shift;
print q{Stereo is on [Volume: } . $self->volume . q{]};
};
sub off {
my $self = shift;
print q{Stereo is off [Volume: } . $self->volume . q{]};
};
sub setCD {
my $self = shift;
$self->state('CD');
};
sub setDVD {
my $self = shift;
$self->state('DVD');
};
sub setRadio {
my $self = shift;
$self->state('Radio');
};
package TV;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'tvChannel'
=> as Int
=> where {
($_ >= 1) && ($_ <= 999);
};
has 'channel' => (
is => 'rw',
isa => 'tvChannel',
default => 7,
trigger => sub {print "Channel " . $_[0]->channel},
);
sub on {
my $self = shift;
print q{TV is on.};
};
sub off {
my $self = shift;
print q{TV is off.};
};
package ApplianceControl;
use Moose;
# Prototyping to see what a Household Device looks like
sub on {
print q{Appliance is switched on.};
};
sub off {
print q{Appliance is switched off.};
};

package EarnedValue;
use Moose;
use OLE;
use Win32::OLE::Const "Microsoft Excel";
use File::Spec;
use Moose::Util::TypeConstraints;
use Params::Coerce ();
use Data::Dumper;
use Exception::Class (
'EV::Exception::Base' => {
description => 'Base Exception Class',
},
'EV::Exception::Base::Logger' => {
isa => 'EV::Exception::Base',
description => 'Simply logging!',
},
'EV::Exception::Base::InvalidState' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Invalid state',
},
'EV::Exception::Base::MissingData' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Incomplete information',
},
);
local $SIG{__DIE__} = sub {
my $err = shift;
if ($err->isa('EV::Exception::Base')) {
die $err;
} elsif ($err->isa('Cost::Exception::Base')) {
die $err;
} elsif ($err->isa('Schedule::Exception::Base')) {
die $err;
} else {
EV::Exception::Base->throw($@);
}
print $@;
};
subtype 'extantFile'
=> as Str
=> where {
( -e $_ );
};
has 'activeSpreadsheet' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_activeSpreadsheet',
# trigger => \&timeStamped_copy
);
has 'Spreadsheet_Schedule' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetSchedule',
default => \&evGantt,
);
has 'Spreadsheet_Cost' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetCost',
default => \&evBasecamp,
);
has 'EV' => (
is => 'rw',
isa => 'Num',
default => \&calculateEV,
lazy => 1,
);
has 'PV' => (
is => 'rw',
isa => 'Num',
default => \&calculatePV,
lazy => 1,
);
has 'AC' => (
is => 'rw',
isa => 'Num',
default => \&calculateAC,
lazy => 1,
);
has 'SPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateSPI,
lazy => 1,
);
has 'CPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateCPI,
lazy => 1,
);
has 'MySchedule' => (
is => 'rw',
isa => 'Schedule',
);
has 'MyCost' => (
is => 'rw',
isa => 'Cost',
);
has 'Diary' => (
is => 'rw',
isa => 'Str',
default => \&evLog,
predicate => 'has_Diary',
clearer => 'clear_Diary',
);
subtype 'CellRange'
=> as Str
=> where {
( /^[a-zA-Z]{1,2}\d+$/ || /^[a-zA-Z]{1,2}\d+?:[a-zA-Z]{1,2}\d+$/);
};
has 'Range' => (
is => 'rw',
# isa => 'CellRange',
isa => 'Str',
predicate => 'has_Range',
clearer => 'clear_Range',
);
has 'multiplying_factor' => (
is => 'rw',
isa => 'Num',
default => 1000,
);
sub calculateEV {
my $self = shift;
my $EV;
eval {
$EV = $self->MySchedule->tally_EVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}
return $EV;
};
sub calculatePV {
my $self = shift;
my $PV;
eval {
$PV = $self->MySchedule->tally_PVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}
return $PV;
};
sub calculateAC {
my $self = shift;
my $AC;
eval {
$AC = $self->MyCost->tally_ACnumbers;
};
my $e;
if ($e = Cost::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
};
return $AC;
};
sub calculateSPI {
my $self = shift;
my ($netEV,$netPV);
my $SPI;
eval {
$netEV = $self->EV;
$netPV = $self->PV;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$SPI = $netEV/$netPV;
};
return $SPI;
};
sub calculateCPI {
my $self = shift;
my ($netEV,$netAC);
my $CPI;
eval {
$netEV = $self->EV;
$netAC = $self->AC;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$CPI = $netEV/$netAC;
};
return $CPI;
};
sub printExecutiveSummary {
my $self = shift;
printf("%10s %12s\n", 'ITEM', 'INR Value');
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'EV', $self->EV);
printf("%10s %12.2f\n", 'PV', $self->PV);
printf("%10s %12.2f\n", 'AC', $self->AC);
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'SPI', $self->SPI);
printf("%10s %12.2f\n", 'CPI', $self->CPI);
printf("%10s-%12s\n", '----------', '------------');
print "\n";
$self->log_Me if $self->has_Diary;
};
sub printCostStructure {
my $self = shift;
my $dashboard;
return unless $self->MyCost->can('ActivityCost');
$dashboard = $self->MyCost->ActivityCost;
printf(" %10s %12s\n", 'ACTIVITY', 'INR Value');
printf(" %10s-%12s\n", '----------', '------------');
my @theItems = sort(keys(%{$dashboard}));
foreach my $thisItem (@theItems) {
printf(" %-10s %12.2f\n", $thisItem, $dashboard->{$thisItem});
};
printf(" %10s-%12s\n", '----------', '------------');
return $dashboard;
}
sub queryMe {
# method returns contents of a cell or the cells in a specified range as 2D array.
my $self = shift; my $sheet = shift;
return unless $self->has_Range;
$sheet = 1 unless $sheet;
my $myRange = $self->Range;
if ($myRange =~ /:/ ) {
$self->_querySpreadsheet_Range($myRange, $sheet);
} else {
$self->_querySpreadsheet_singleCell($myRange, $sheet);
};
};
sub _querySpreadsheet_singleCell {
# method returns contents of a single cell.
my $self = shift;
my $cell = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;
my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;
my $workbook = $excel->Workbooks->Open($source) || die "Unable to open!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);
my $data = $evSheet->Range($cell); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $cell . ' | ' . $dataValue . ' |';
$workbook->Close;
return $dataValue;
};
sub _querySpreadsheet_Range {
# method returns contents of a range of cells (2D array).
my $self = shift;
my $range = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;
my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;
my $workbook = $excel->Workbooks->Open($source) || die "Unable to open ($source)!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);
my $data = $evSheet->Range($range); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $range . ' |';
$workbook->Close;
return $dataValue;
};
sub log_Me {
my $self = shift;
my $e = shift;
my $diary = $self->Diary;
my $eMsg = '';
open(INFO, ">>$diary");
unless ($e) {
print INFO &timeStamp, " OK ";
eval {
EV::Exception::Base::Logger->throw;
};
my $e;
if ($e = EV::Exception::Base::Logger->caught) {
my @lines = ();
foreach my $thisLine (split("\n", $e->trace)) {
push @lines, $thisLine if ($thisLine =~ 'EarnedValue::');
}
print INFO join(' | ', @lines);
} #fi
} elsif ($e->isa('Cost::Exception::Base')) {
$eMsg = Cost::Exception::Base->description . " -> ";
$eMsg .= Cost::Exception::Base::MissingData->description
if $e->isa('Cost::Exception::Base::MissingData');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
} elsif ($e->isa('Schedule::Exception::Base')) {
$eMsg = Schedule::Exception::Base->description . " ";
$eMsg .= Schedule::Exception::Base::InvalidState->description
if $e->isa('Schedule::Exception::Base::InvalidState');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
}
close INFO;
}
package EarnedValue;
use Moose;
use OLE;
use Win32::OLE::Const "Microsoft Excel";
use File::Spec;
use Moose::Util::TypeConstraints;
use Params::Coerce ();
use Data::Dumper;
use Exception::Class (
'EV::Exception::Base' => {
description => 'Base Exception Class',
},
'EV::Exception::Base::Logger' => {
isa => 'EV::Exception::Base',
description => 'Simply logging!',
},
'EV::Exception::Base::InvalidState' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Invalid state',
},
'EV::Exception::Base::MissingData' => {
isa => 'EV::Exception::Base',
fields => [ 'EV', 'PV', 'AC'],
description => 'Incomplete information',
},
);
local $SIG{__DIE__} = sub {
my $err = shift;
if ($err->isa('EV::Exception::Base')) {
die $err;
} elsif ($err->isa('Cost::Exception::Base')) {
die $err;
} elsif ($err->isa('Schedule::Exception::Base')) {
die $err;
} else {
EV::Exception::Base->throw($@);
}
print $@;
};
subtype 'extantFile'
=> as Str
=> where {
( -e $_ );
};
has 'activeSpreadsheet' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_activeSpreadsheet',
# trigger => \&timeStamped_copy
);
has 'Spreadsheet_Schedule' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetSchedule',
default => \&evGantt,
);
has 'Spreadsheet_Cost' => (
is => 'rw',
isa => 'extantFile',
predicate => 'has_spreadsheetCost',
default => \&evBasecamp,
);
has 'Diary' => (
is => 'rw',
isa => 'Str',
default => \&evLog,
predicate => 'has_Diary',
clearer => 'clear_Diary',
);
has 'EV' => (
is => 'rw',
isa => 'Num',
default => \&calculateEV,
lazy => 1,
);
has 'PV' => (
is => 'rw',
isa => 'Num',
default => \&calculatePV,
lazy => 1,
);
has 'AC' => (
is => 'rw',
isa => 'Num',
default => \&calculateAC,
lazy => 1,
);
has 'SPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateSPI,
lazy => 1,
);
has 'CPI' => (
is => 'rw',
isa => 'Num',
default => \&calculateCPI,
lazy => 1,
);
has 'MySchedule' => (
is => 'rw',
isa => 'Schedule',
);
has 'MyCost' => (
is => 'rw',
isa => 'Cost',
);
has 'multiplying_factor' => (
is => 'rw',
isa => 'Num',
default => 1000,
);
subtype 'CellRange'
=> as Str
=> where {
( /^[a-zA-Z]{1,2}\d+$/ || /^[a-zA-Z]{1,2}\d+?:[a-zA-Z]{1,2}\d+$/);
};
has 'Range' => (
is => 'rw',
# isa => 'CellRange',
isa => 'Str',
predicate => 'has_Range',
clearer => 'clear_Range',
);
sub calculateEV {
my $self = shift;
my $EV;
eval {
$EV = $self->MySchedule->tally_EVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}
return $EV;
};
sub calculatePV {
my $self = shift;
my $PV;
eval {
$PV = $self->MySchedule->tally_PVnumbers->[0] * $self->multiplying_factor;
};
my $e;
if ($e = Schedule::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
}
return $PV;
};
sub calculateAC {
my $self = shift;
my $AC;
eval {
$AC = $self->MyCost->tally_ACnumbers;
};
my $e;
if ($e = Cost::Exception::Base->caught) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
};
return $AC;
};
sub calculateSPI {
my $self = shift;
my ($netEV,$netPV);
my $SPI;
eval {
$netEV = $self->EV;
$netPV = $self->PV;
};
my $e;
if ( ($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$SPI = $netEV/$netPV;
};
return $SPI;
};
sub calculateCPI {
my $self = shift;
my ($netEV,$netAC);
my $CPI;
eval {
$netEV = $self->EV;
$netAC = $self->AC;
};
my $e;
if (($e = Cost::Exception::Base->caught)
|| ($e = Schedule::Exception::Base->caught)) {
$self->log_Me($e);
} else {
$self->log_Me if $self->has_Diary;
$CPI = $netEV/$netAC;
};
return $CPI;
};
sub printExecutiveSummary {
my $self = shift;
printf("%10s %12s\n", 'ITEM', 'INR Value');
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'EV', $self->EV);
printf("%10s %12.2f\n", 'PV', $self->PV);
printf("%10s %12.2f\n", 'AC', $self->AC);
printf("%10s-%12s\n", '----------', '------------');
printf("%10s %12.2f\n", 'SPI', $self->SPI);
printf("%10s %12.2f\n", 'CPI', $self->CPI);
printf("%10s-%12s\n", '----------', '------------');
print "\n";
$self->log_Me if $self->has_Diary;
};
sub printCostStructure {
my $self = shift;
my $dashboard;
return unless $self->MyCost->can('ActivityCost');
$dashboard = $self->MyCost->ActivityCost;
printf(" %10s %12s\n", 'ACTIVITY', 'INR Value');
printf(" %10s-%12s\n", '----------', '------------');
my @theItems = sort(keys(%{$dashboard}));
foreach my $thisItem (@theItems) {
printf(" %-10s %12.2f\n", $thisItem, $dashboard->{$thisItem});
};
printf(" %10s-%12s\n", '----------', '------------');
return $dashboard;
}
sub evGantt {
my $folder = File::Spec->curdir();
my $file = q{Dashboard.xls};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
};
sub evBasecamp {
my $folder = File::Spec->curdir();
my $file = q{time-export.csv};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
}
sub evLog {
my $folder = File::Spec->curdir();
my $file = q{main.log};
return File::Spec->rel2abs(File::Spec->catfile($folder, $file));
};
sub timeStamped_copy {
# for safety, make a time-stamped copy before the spreadsheet is accessed.
my $self = shift;
};
sub queryMe {
# method returns contents of a cell or the cells in a specified range as 2D array.
my $self = shift; my $sheet = shift;
return unless $self->has_Range;
$sheet = 1 unless $sheet;
my $myRange = $self->Range;
if ($myRange =~ /:/ ) {
$self->_querySpreadsheet_Range($myRange, $sheet);
} else {
$self->_querySpreadsheet_singleCell($myRange, $sheet);
};
};
sub _querySpreadsheet_singleCell {
# method returns contents of a single cell.
my $self = shift;
my $cell = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;
my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;
my $workbook = $excel->Workbooks->Open($source) || die "Unable to open!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);
my $data = $evSheet->Range($cell); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $cell . ' | ' . $dataValue . ' |';
$workbook->Close;
return $dataValue;
};
sub _querySpreadsheet_Range {
# method returns contents of a range of cells (2D array).
my $self = shift;
my $range = shift;
my $sheet = shift;
$sheet = 1 unless $sheet;
my $excel = OLE->CreateObject("Excel.Application");
my $source = $self->activeSpreadsheet;
my $workbook = $excel->Workbooks->Open($source) || die "Unable to open ($source)!";
my $evSheet_namu = $workbook->Worksheets($sheet)->{Name};
my $evSheet = $workbook->Worksheets($evSheet_namu);
my $data = $evSheet->Range($range); my $dataValue = $data->{Value};
# print '| ' . $evSheet_namu . ' | ' . $range . ' |';
$workbook->Close;
return $dataValue;
};
sub log_Me {
my $self = shift;
my $e = shift;
my $diary = $self->Diary;
my $eMsg = '';
open(INFO, ">>$diary");
unless ($e) {
print INFO &timeStamp, " OK ";
eval {
EV::Exception::Base::Logger->throw;
};
my $e;
if ($e = EV::Exception::Base::Logger->caught) {
my @lines = ();
foreach my $thisLine (split("\n", $e->trace)) {
push @lines, $thisLine if ($thisLine =~ 'EarnedValue::');
}
print INFO join(' | ', @lines);
} #fi
} elsif ($e->isa('Cost::Exception::Base')) {
$eMsg = Cost::Exception::Base->description . " -> ";
$eMsg .= Cost::Exception::Base::MissingData->description
if $e->isa('Cost::Exception::Base::MissingData');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
} elsif ($e->isa('Schedule::Exception::Base')) {
$eMsg = Schedule::Exception::Base->description . " ";
$eMsg .= Schedule::Exception::Base::InvalidState->description
if $e->isa('Schedule::Exception::Base::InvalidState');
print INFO &timeStamp, " ERROR ";
print INFO $e->error;
print INFO $e->trace;
print INFO $eMsg;
}
close INFO;
}
sub timeStamp {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
my $tstamp = sprintf
"\[%02d\-%02d\-%4d : %02d\-%02d\-%02d\]",
mon+1,$mday,$year+1900,$hour,$min,$sec;
return $tstamp;
}