From 4e1f8bd5d53f3473ceaf0d34db1875dc519d4259 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 21 Jan 2009 10:07:45 -0800 Subject: [PATCH 01/31] Add file to install Passive Analytics in the db and config file. --- sbin/installPassiveAnalytics.pl | 242 ++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 sbin/installPassiveAnalytics.pl diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl new file mode 100644 index 000000000..4569fa992 --- /dev/null +++ b/sbin/installPassiveAnalytics.pl @@ -0,0 +1,242 @@ +#------------------------------------------------------------------- +# PassiveAnalytics is Copyright 2008-2009 SDH Consulting +#------------------------------------------------------------------- + +$|++; # disable output buffering +our ($webguiRoot, $configFile, $help, $man); + +BEGIN { + $webguiRoot = ".."; + unshift (@INC, $webguiRoot."/lib"); +} + +use strict; +use Pod::Usage; +use Getopt::Long; +use WebGUI::Session; +use WebGUI::PassiveAnalytics::Rule; +use WebGUI::Utility; + +# Get parameters here, including $help +GetOptions( + 'configFile=s' => \$configFile, + 'help' => \$help, + 'man' => \$man, +); + +pod2usage( verbose => 1 ) if $help; +pod2usage( verbose => 2 ) if $man; +pod2usage( msg => "Must specify a config file!" ) unless $configFile; + +my $session = start( $webguiRoot, $configFile ); +# Do your work here +#installLoggingTables($session); +#installPassiveAnalyticsRule($session); +#installPassiveAnalyticsConfig($session); +installWorkflow($session); +#addAdminGroup($session); + +finish($session); + +#---------------------------------------------------------------------------- +# Your sub here + +sub installLoggingTables { + my $session = shift; + print "\tInstall logging tables... "; + my $db = $session->db; + $db->write(<write(<write(<write(<write(<write(<crud_createTable($session); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the Passive Analytics config file entry +# for the adminConsole and the content handler +sub installPassiveAnalyticsConfig { + my $session = shift; + print "\tAdd Passive Analytics entry to the config file... "; + # Admin Bar/Console + my $adminConsole = $session->config->get('adminConsole'); + if (!exists $adminConsole->{'passiveAnalytics'}) { + $adminConsole->{'passiveAnalytics'} = { + "icon" => "statistics.gif", + "uiLevel" => 1, + "url" => "^PageUrl(\"\",op=passiveAnalytics;func=editRuleflow);", + "title" => "^International(Passive Analytics,PassiveAnalytics);", + "groupSetting" => "3", + }; + $session->config->set('adminConsole', $adminConsole); + } + # Content Handler + my $contentHandlers = $session->config->get('contentHandlers'); + if (!isIn('WebGUI::Content::PassiveAnalytics',@{ $contentHandlers} ) ) { + my $contentIndex = 0; + HANDLER: while ($contentIndex <= $#{ $contentHandlers } ) { + print $contentHandlers->[$contentIndex]."\n"; + ##Insert before Operation + if($contentHandlers->[$contentIndex] eq 'WebGUI::Content::Operation') { + splice @{ $contentHandlers }, $contentIndex, 0, 'WebGUI::Content::PassiveAnalytics'; + last HANDLER; + } + ++$contentIndex; + } + $session->config->set('contentHandlers', $contentHandlers); + } + # Workflow Activities + my $workflowActivities = $session->config->get('workflowActivities'); + my @none = @{ $workflowActivities->{'None'} }; + if (!isIn('WebGUI::Workflow::Activity::SummarizePassiveAnalytics', @none)) { + push @none, 'WebGUI::Workflow::Activity::SummarizePassiveAnalytics'; + } + if (!isIn('WebGUI::Workflow::Activity::BucketPassiveAnalytics', @none)) { + push @none, 'WebGUI::Workflow::Activity::BucketPassiveAnalytics'; + } + $workflowActivities->{'None'} = [ @none ]; + $session->config->set('workflowActivities', $workflowActivities); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the Passive Analytics Workflow +sub installWorkflow { + my $session = shift; + print "\tAdd Passive Analytics Workflow... "; + my $workflow = WebGUI::Workflow->create( + $session, + { + title => 'Analyze Passive Analytics', + mode => 'singleton', + type => 'None', + description => 'Manual changes to this workflow will be lost. Please only use the Passive Analytics screen to make changes', + }, + 'PassiveAnalytics000001', + ); + my $summarize = $workflow->addActivity('WebGUI::Workflow::Activity::SummarizePassiveAnalytics'); + my $bucket = $workflow->addActivity('WebGUI::Workflow::Activity::BucketPassiveAnalytics'); + $summarize->set('title', 'Perform duration analysis'); + $bucket->set( 'title', 'Please log entries into buckets'); + $workflow->update({enabled => 1}); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +sub start { + my $webguiRoot = shift; + my $configFile = shift; + my $session = WebGUI::Session->open($webguiRoot,$configFile); + $session->user({userId=>3}); + + ## If your script is adding or changing content you need these lines, otherwise leave them commented + # + # my $versionTag = WebGUI::VersionTag->getWorking($session); + # $versionTag->set({name => 'Name Your Tag'}); + # + ## + + return $session; +} + +#---------------------------------------------------------------------------- +sub finish { + my $session = shift; + + ## If your script is adding or changing content you need these lines, otherwise leave them commented + # + # my $versionTag = WebGUI::VersionTag->getWorking($session); + # $versionTag->commit; + ## + + $session->var->end; + $session->close; +} + +__END__ + + +=head1 NAME + +utility - A template for WebGUI utility scripts + +=head1 SYNOPSIS + + utility --configFile config.conf ... + + utility --help + +=head1 DESCRIPTION + +This WebGUI utility script helps you... + +=head1 ARGUMENTS + +=head1 OPTIONS + +=over + +=item B<--configFile config.conf> + +The WebGUI config file to use. Only the file name needs to be specified, +since it will be looked up inside WebGUI's configuration directory. +This parameter is required. + +=item B<--help> + +Shows a short summary and usage + +=item B<--man> + +Shows this document + +=back + +=head1 AUTHOR + +Copyright 2001-2008 Plain Black Corporation. + +=cut + +#vim:ft=perl From d59cdea8a7b3a81cddf0c3bb118c94835893c42b Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 21 Jan 2009 10:13:24 -0800 Subject: [PATCH 02/31] Add logging to the Asset Content handler. --- lib/WebGUI/Content/Asset.pm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/WebGUI/Content/Asset.pm b/lib/WebGUI/Content/Asset.pm index 3d5e6ba74..64242302e 100644 --- a/lib/WebGUI/Content/Asset.pm +++ b/lib/WebGUI/Content/Asset.pm @@ -170,6 +170,17 @@ sub page { $method = "view"; } } + ##Passive Analytics Logging + my $assetClass = $asset->get('className'); + $assetClass =~ s/^WebGUI::Asset:://; + if ( $assetClass ne 'Snippet' + && substr($assetClass,0,4) ne 'File') { + $session->db->write( + q|INSERT INTO `passiveLog` (userId, sessionId, assetId, timestamp, url) VALUES (?,?,?,?,?)|, + [ $session->user->userId, $session->getId, $asset->getId, time(), $session->request->uri,] + ); + } + $output = tryAssetMethod($session,$asset,$method); $output = tryAssetMethod($session,$asset,"view") unless ($output || ($method eq "view")); } From eb844fd26e4a292f8a72cc6985f327f861d161ad Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 21 Jan 2009 10:19:08 -0800 Subject: [PATCH 03/31] Add Passive Analytics modules, Workflow Activites, i18n and content handler. --- lib/WebGUI/Content/PassiveAnalytics.pm | 49 ++++ lib/WebGUI/PassiveAnalytics/Flow.pm | 254 ++++++++++++++++++ lib/WebGUI/PassiveAnalytics/Rule.pm | 92 +++++++ .../Activity/BucketPassiveAnalytics.pm | 138 ++++++++++ .../Activity/SummarizePassiveAnalytics.pm | 158 +++++++++++ lib/WebGUI/i18n/English/PassiveAnalytics.pm | 99 +++++++ 6 files changed, 790 insertions(+) create mode 100644 lib/WebGUI/Content/PassiveAnalytics.pm create mode 100644 lib/WebGUI/PassiveAnalytics/Flow.pm create mode 100644 lib/WebGUI/PassiveAnalytics/Rule.pm create mode 100644 lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm create mode 100644 lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm create mode 100644 lib/WebGUI/i18n/English/PassiveAnalytics.pm diff --git a/lib/WebGUI/Content/PassiveAnalytics.pm b/lib/WebGUI/Content/PassiveAnalytics.pm new file mode 100644 index 000000000..7de3b86a8 --- /dev/null +++ b/lib/WebGUI/Content/PassiveAnalytics.pm @@ -0,0 +1,49 @@ +package WebGUI::Content::PassiveAnalytics; + +use strict; +use WebGUI::AdminConsole; +use WebGUI::Exception; +use WebGUI::PassiveAnalytics::Flow; + +=head1 NAME + +Package WebGUI::Content::PassiveAnalytics + +=head1 DESCRIPTION + +Handle all requests for building and editing Passive Analytic flows. + +=head1 SYNOPSIS + + use WebGUI::Content::PassiveAnalytics; + my $output = WebGUI::Content::PassiveAnalytics::handler($session); + +=head1 SUBROUTINES + +These subroutines are available from this package: + +=cut + +#------------------------------------------------------------------- + +=head2 handler ( session ) + +The content handler for this package. + +=cut + +sub handler { + my ($session) = @_; + my $output = undef; + return undef unless $session->form->get('op') eq 'passiveAnalytics'; + my $function = "www_".$session->form->get('func'); + if ($function ne "www_" && (my $sub = WebGUI::PassiveAnalytics::Flow->can($function))) { + $output = $sub->($session); + } + else { + WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $function inside PassiveAnalytics", method=>$function); + } + return $output; +} + +1; diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm new file mode 100644 index 000000000..143a24b66 --- /dev/null +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -0,0 +1,254 @@ +package WebGUI::PassiveAnalytics::Flow; + +use strict; +use Tie::IxHash; +use WebGUI::AdminConsole; +use WebGUI::HTMLForm; +use WebGUI::International; +use WebGUI::Pluggable; +use WebGUI::PassiveAnalytics::Rule; +use WebGUI::Utility; +use WebGUI::HTMLForm; +use WebGUI::Workflow; +use WebGUI::Workflow::Instance; + +=head1 NAME + +Package WebGUI::PassiveAnalytics::Flow + +=head1 DESCRIPTION + +Web interface for making sets of rules for doing passive analytics, and +running them. + +=cut + +#---------------------------------------------------------------------------- + +=head2 canView ( session [, user] ) + +Returns true if the user can administrate this operation. user defaults to +the current user. + +=cut + +sub canView { + my $session = shift; + my $user = shift || $session->user; + return $user->isInGroup( 3 ); +} + +#------------------------------------------------------------------- + +=head2 www_deleteRule ( ) + +Deletes an activity from a workflow. + +=cut + +sub www_deleteRule { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId")); + if (defined $rule) { + $rule->delete; + } + return www_editRuleflow($session); +} + +#------------------------------------------------------------------ + +=head2 www_demoteRule ( session ) + +Moves a Rule down one position in the execution order. + +=head3 session + +A reference to the current session. + +=cut + +sub www_demoteRule { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId")); + if (defined $rule) { + $rule->demote; + } + return www_editRuleflow($session); +} + +#------------------------------------------------------------------- + +=head2 www_editRuleflow ( session ) + +Configure a set of analyses to run on the passive logs. The analysis is destructive. + +=cut + +sub www_editRuleflow { + my $session = shift; + my $error = shift; + return $session->privilege->insufficient() unless canView($session); + if ($error) { + $error = qq|
$error
\n|; + } + my $i18n = WebGUI::International->new($session, "PassiveAnalytics"); + my $addmenu = '
'; + $addmenu .= sprintf '%s', + $session->url->page('op=passiveAnalytics;func=editRule'), + $i18n->get('Add a bucket'); + $addmenu .= '
'; + my $f = WebGUI::HTMLForm->new($session); + $f->hidden( + name=>'op', + value=>'passiveAnalytics' + ); + $f->hidden( + name=>'func', + value=>'editRuleflowSave' + ); + $f->integer( + name => 'pauseInterval', + value => 300, + label => $i18n->get('pause interval'), + hoverHelp => $i18n->get('pause interval help'), + ); + $f->submit(value => $i18n->get('Begin analysis')); + my $steps = ''; + my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session); + my $icon = $session->icon; + while (my $rule = $getARule->()) { + my $id = $rule->getId; + my $bucket = $rule->get('bucketName'); + $steps .= ''; + + } + $steps .= '
' + . $icon->delete( 'op=passiveAnalytics;func=deleteRule;ruleId='.$id, undef, $i18n->get('confirm delete rule')) + . $icon->edit( 'op=passiveAnalytics;func=editRule;ruleId='.$id) + . $icon->moveDown('op=passiveAnalytics;func=demoteRule;ruleId='.$id) + . $icon->moveUp( 'op=passiveAnalytics;func=promoteRule;ruleId='.$id) + . ''.$bucket.'
 Other
'; + my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics'); +} + +#------------------------------------------------------------------- + +=head2 www_editRuleflowSave ( ) + +Saves the results of www_editRuleflow() + +=cut + +sub www_editRuleflowSave { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $workflow = WebGUI::Workflow->new($session, 'PassiveAnalytics000001'); + return www_editRuleflow($session, "The Passive Analytics workflow has been deleted. Please contact an Administrator immediately.") unless defined $workflow; + my $delta = $session->form->process('pauseInterval','integer'); + my $activities = $workflow->getActivities(); + ##Note, they're in order, and the order is known. + $activities->[0]->set('deltaInterval', $delta); + $activities->[1]->set('userId', $session->user->userId); + my $instance = WebGUI::Workflow::Instance->create($session, { + workflowId => $workflow->getId, + priority => 1, + }); + if (!defined $instance) { + return www_editRuleflow($session, "A Passive Analytics analysis is currently running.") if $session->stow->get('singletonWorkflowClash'); + return www_editRuleflow($session, "Error creating the workflow instance."); + } + $instance->start('skipRealtime'); + return www_editRuleflow($session, "Passive Analytics session started"); +} + + +#------------------------------------------------------------------- + +=head2 www_editRule ( ) + +Displays a form to edit the properties rule. + +=cut + +sub www_editRule { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + + ##Make a PassiveAnalytics rule to use to populate the form. + my $ruleId = $session->form->get('ruleId'); + my $rule; + if ($ruleId) { + $rule = WebGUI::PassiveAnalytics::Rule->new($session, $ruleId); + } + else { + ##We need a temporary rule so that we can call dynamicForm, below + $ruleId = 'new'; + $rule = WebGUI::PassiveAnalytics::Rule->create($session, {}); + } + + ##Build the form + my $form = WebGUI::HTMLForm->new($session); + $form->hidden( name=>"op", value=>"passiveAnalytics"); + $form->hidden( name=>"func", value=>"editRuleSave"); + $form->hidden( name=>"ruleId", value=>$ruleId); + $form->dynamicForm([WebGUI::PassiveAnalytics::Rule->crud_definition($session)], 'properties', $rule); + $form->submit; + + my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); + my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + $ac->addSubmenuItem($session->url->page("op=passiveAnalytics;func=editRuleflow"), $i18n->get("manage ruleset")); + if ($ruleId eq 'new') { + $rule->delete; + } + return $ac->render($form->print,$i18n->get('Edit Rule')); +} + +#------------------------------------------------------------------- + +=head2 www_editRuleSave ( ) + +Saves the results of www_editRule(). + +=cut + +sub www_editRuleSave { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $ruleId = $session->form->get('ruleId'); + my $rule; + if ($ruleId eq 'new') { + $rule = WebGUI::PassiveAnalytics::Rule->create($session, {}); + } + else { + $rule = WebGUI::PassiveAnalytics::Rule->new($session, $ruleId); + } + $rule->updateFromFormPost if $rule; + return www_editRuleflow($session); +} + +#------------------------------------------------------------------ + +=head2 www_promoteRule ( session ) + +Moves a rule up one position in the execution order. + +=head3 session + +A reference to the current session. + +=cut + +sub www_promoteRule { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $rule = WebGUI::PassiveAnalytics::Rule->new($session, $session->form->get("ruleId")); + if (defined $rule) { + $rule->promote; + } + return www_editRuleflow($session); +} + +1; diff --git a/lib/WebGUI/PassiveAnalytics/Rule.pm b/lib/WebGUI/PassiveAnalytics/Rule.pm new file mode 100644 index 000000000..5d052c8da --- /dev/null +++ b/lib/WebGUI/PassiveAnalytics/Rule.pm @@ -0,0 +1,92 @@ +package WebGUI::PassiveAnalytics::Rule; + +use base qw/WebGUI::Crud/; +use WebGUI::International; + +=head1 NAME + +Package WebGUI::PassiveAnalytics::Rule; + +=head1 DESCRIPTION + +Base class for rules that are used to analyze the Passive Analytics log. + +=head1 METHODS + +These methods are available from this class: + +=cut + +#------------------------------------------------------------------- + +=head2 crud_definition ( ) + +WebGUI::Crud definition for this class. + +=head3 tableName + +analyticRule. + +=head3 tableKey + +ruleId + +=head3 sequenceKey + +None. There is only 1 sequence of rules for a site. + +=head3 properties + +=head4 bucketName + +The name of a bucket to hold results for this rule. + +=head4 rules + +JSON blob with configuration data for the individual rules. + +=cut + +sub crud_definition { + my ($class, $session) = @_; + my $definition = $class->SUPER::crud_definition($session); + $definition->{tableName} = 'analyticRule'; + $definition->{tableKey} = 'ruleId'; + $definition->{sequenceKey} = ''; + my $properties = $definition->{properties}; + my $i18n = WebGUI::International->new($session); + $properties->{bucketName} = { + fieldType => 'text', + label => $i18n->get('Bucket Name','PassiveAnalytics'), + hoverHelp => $i18n->get('Bucket Name help','PassiveAnalytics'), + defaultValue => '', + }; + $properties->{regexp} = { + fieldType => 'text', + label => $i18n->get('regexp','PassiveAnalytics'), + hoverHelp => $i18n->get('regexp help','PassiveAnalytics'), + defaultValue => '.+', + }; + return $definition; +} + +#------------------------------------------------------------------- + +=head2 matchesBucket ( $logLine ) + +Executes the rule to determine if a log file entry matches the rule. + +=head3 $logLine + +A hashref of information from 1 line of the logs. + +=cut + +sub matchesBucket { + my ($self, $logLine) = @_; + my $regexp = $self->get('regexp'); + return $logLine->{url} =~ m/$regexp/; +} + +1; +#vim:ft=perl diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm new file mode 100644 index 000000000..170d50dfb --- /dev/null +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -0,0 +1,138 @@ +package WebGUI::Workflow::Activity::BucketPassiveAnalytics; + + +=head1 LEGAL + + ------------------------------------------------------------------- + Copyright 2001-2008 SDH Corporation + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Workflow::Activity'; +use WebGUI::PassiveAnalytics::Rule; +use WebGUI::Inbox; + +=head1 NAME + +Package WebGUI::Workflow::Activity::BucketPassiveAnalytics + +=head1 DESCRIPTION + +Run through a set of rules to figure out how to classify log file entries. + +=head1 SYNOPSIS + +See WebGUI::Workflow::Activity for details on how to use any activity. + +=head1 METHODS + +These methods are available from this class: + +=cut + + +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +See WebGUI::Workflow::Activity::defintion() for details. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, "PassiveAnalytics"); + push( @{$definition}, { + name=>$i18n->get("Bucket Passive Analytics"), + properties=> { + notifyUser => { + fieldType => 'user', + label => $i18n->get('User'), + hoverHelp => $i18n->get('User help'), + defaultValue => $session->user->userId, + }, + }, + }); + return $class->SUPER::definition($session,$definition); +} + + +#------------------------------------------------------------------- + +=head2 execute ( [ object ] ) + +Analyze the deltaLog table, and generate the bucketLog table. + +=head3 notes + +=cut + +sub execute { + my ($self, undef, $instance) = @_; + my $session = $self->session; + my $endTime = time() + $self->getTTL; + my $expired = 0; + + ##Load all the rules into an array + my @rules = (); + my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session); + while (my $rule = $getARule->()) { + push @rules, $rule; + } + + ##Get the index stored from the last invocation of the Activity. If this is + ##the first run, then clear out the table. + my $logIndex = $instance->getScratch('lastPassiveLogIndex') || 0; + if ($logIndex == 0) { + $session->db->write('delete from bucketLog'); + } + + ##Configure all the SQL + my $deltaSql = <<"EOSQL1"; +select userId, assetId, url, delta, from_unixtime(timeStamp) as stamp + from deltaLog order by timestamp limit $logIndex, 1234567890 +EOSQL1 + my $deltaSth = $session->db->read($deltaSql); + my $bucketSth = $session->db->prepare('insert into bucketLog (userId, Bucket, duration, timeStamp) VALUES (?,?,?,?)'); + + ##Walk through the log file entries, one by one. Run each entry against + ##all the rules until 1 matches. If it doesn't match any rule, then bin it + ##into the "Other" bucket. + DELTA_ENTRY: while (my $entry = $deltaSth->hashRef()) { + ++$logIndex; + my $bucketFound = 0; + RULE: foreach my $rule (@rules) { + next RULE unless $rule->matchesBucket($entry); + $bucketSth->execute([$entry->{userId}, $rule->get('bucketName'), $entry->{delta}, $entry->{stamp}]); + } + if (!$bucketFound) { + $bucketSth->execute([$entry->{userId}, 'Other', $entry->{delta}, $entry->{stamp}]); + } + if (time() > $endTime) { + $expired = 1; + last DELTA_ENTRY; + } + } + + if ($expired) { + $instance->setScratch('logIndex', $logIndex); + return $self->WAITING(1); + } + my $inbox = WebGUI::Inbox->new($self->session); + $inbox->addMessage({ + status => 'unread', + subject => 'Passive analytics is done', + userId => $self->get('userId'), + message => 'Passive analytics is done', + }); + + return $self->COMPLETE; +} + +1; + +#vim:ft=perl diff --git a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm new file mode 100644 index 000000000..7d5e52f49 --- /dev/null +++ b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm @@ -0,0 +1,158 @@ +package WebGUI::Workflow::Activity::SummarizePassiveAnalytics; + + +=head1 LEGAL + + ------------------------------------------------------------------- + Copyright 2001-2008 SDH Corporation + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Workflow::Activity'; + +=head1 NAME + +Package WebGUI::Workflow::Activity::SummarizePassiveAnalytics + +=head1 DESCRIPTION + +Summarize how long a user stayed on a page, using a user supplied interval. + +=head1 SYNOPSIS + +See WebGUI::Workflow::Activity for details on how to use any activity. + +=head1 METHODS + +These methods are available from this class: + +=cut + + +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +See WebGUI::Workflow::Activity::defintion() for details. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); + push(@{$definition}, { + name=>$i18n->get('activityName'), + properties=> { + deltaInterval => { + fieldType => 'interval', + label => $i18n->get('pause interval'), + defaultValue => 15, + hoverHelp => $i18n->get('pause interval help'), + }, + } + }); + return $class->SUPER::definition($session,$definition); +} + + +#------------------------------------------------------------------- + +=head2 execute ( [ object ] ) + +Analyze the passiveLog table, and generate the deltaLog table. + +=head3 notes + +If there is only 1 line in the table for a particular sessionId or +userId, no conclusions as to how long the user viewed a page can be +drawn from that. Similarly, the last entry in their browsing log +yields no data, since we require another entry in the passiveLog to +determine a delta. + +=cut + +sub execute { + my ($self, undef, $instance) = @_; + my $session = $self->session; + my $endTime = time() + $self->getTTL; + my $deltaInterval = $self->get('deltaInterval'); + + my $passive = 'select * from passiveLog where userId <> 1 order by userId, sessionId, timeStamp'; + my $sth; + my $lastUserId; + my $lastSessionId; + my $lastTimeStamp; + my $lastAssetId; + my $lastUrl; + my $counter = $instance->getScratch('counter'); + if ($counter) { + $passive .= ' limit '. $counter .', 1234567890'; + $sth = $session->db->read($passive); + $lastUserId = $instance->getScratch('lastUserId'); + $lastSessionId = $instance->getScratch('lastSessionId'); + $lastTimeStamp = $instance->getScratch('lastTimeStamp'); + $lastAssetId = $instance->getScratch('lastAssetId'); + $lastUrl = $instance->getScratch('lastUrl'); + } + else { + $sth = $session->db->read($passive); + my $logLine = $sth->hashRef(); + $logLine = $sth->hashRef(); + $lastUserId = $logLine->{userId}; + $lastSessionId = $logLine->{sessionId}; + $lastTimeStamp = $logLine->{timeStamp}; + $lastAssetId = $logLine->{assetId}; + $lastUrl = $logLine->{url}; + } + + $session->db->write('delete from deltaLog'); ##Only if we're starting out + my $deltaLog = $session->db->prepare('insert into deltaLog (userId, assetId, delta, timeStamp, url) VALUES (?,?,?,?,?)'); + + my $expired = 0; + LOG_ENTRY: while (my $logLine = $sth->hashRef()) { + $counter++; + my $delta = $logLine->{timeStamp} - $lastTimeStamp; + if ( $logLine->{userId} eq $lastUserId + && $logLine->{sessionId} eq $lastSessionId + && $delta < $deltaInterval ) { + $deltaLog->execute([$lastUserId, $lastAssetId, $delta, $lastTimeStamp, $lastUrl]); + } + $lastUserId = $logLine->{userId}; + $lastSessionId = $logLine->{sessionId}; + $lastTimeStamp = $logLine->{timeStamp}; + $lastAssetId = $logLine->{assetId}; + $lastUrl = $logLine->{url}; + if (time() > $endTime) { + $instance->setScratch('lastUserId', $lastUserId); + $instance->setScratch('lastSessionId', $lastSessionId); + $instance->setScratch('lastTimeStamp', $lastTimeStamp); + $instance->setScratch('lastAssetId', $lastAssetId); + $instance->setScratch('lastUrl', $lastUrl); + $instance->setScratch('counter', $counter); + $expired = 1; + last LOG_ENTRY; + } + } + + if ($expired) { + return $self->WAITING(1); + } + + $instance->deleteScratch('lastUserId'); + $instance->deleteScratch('lastSessionId'); + $instance->deleteScratch('lastTimeStamp'); + $instance->deleteScratch('lastAssetId'); + $instance->deleteScratch('lastUrl'); + $instance->deleteScratch('counter'); + return $self->COMPLETE; +} + + + +1; + +#vim:ft=perl diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm new file mode 100644 index 000000000..b46a12eca --- /dev/null +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -0,0 +1,99 @@ +package WebGUI::i18n::English::PassiveAnalytics; + +use strict; + +our $I18N = { + + 'Summarize Passive Analytics' => { + message => q|Summarize Passive Analytics|, + context => q|The name of this workflow activity.|, + lastUpdated => 0, + }, + + 'pause interval' => { + message => q|Pause threshold|, + lastUpdated => 0, + }, + + 'pause interval help' => { + message => q|Set the time between clicks that is interpreted as the user reading the page, as opposed to beginning a new browsing session, or leaving the site.|, + lastUpdated => 0, + }, + + 'other' => { + message => q|Other|, + lastUpdated => 0, + context => q|Meaning not like anything in a set. This, that and the other one. Also, a catch all.| + }, + + 'Bucket Name' => { + message => q|Bucket Name|, + lastUpdated => 0, + context => q|To name a container, or bucket.| + }, + + 'Bucket Name help' => { + message => q|Pick a unique, descriptive short name for this bucket.|, + lastUpdated => 0, + context => q|| + }, + + 'regexp' => { + message => q|Regular expression|, + lastUpdated => 0, + context => q|| + }, + + 'regexp help' => { + message => q|Define a regular expression to pick log entries for this bucket.
+^ = beginning of url
+$ = end of url
+. = any character
+* = any amount
++ = 1 or more
+? = 0 or 1
+|, + lastUpdated => 0, + context => q|| + }, + + 'Passive Analytics' => { + message => q|Passive Analytics|, + lastUpdated => 0, + context => q|| + }, + + 'Edit Rule' => { + message => q|Edit Rule|, + lastUpdated => 0, + context => q|| + }, + + 'Add a bucket' => { + message => q|Add a bucket|, + lastUpdated => 0, + context => q|| + }, + + 'User' => { + message => q|User|, + lastUpdated => 0, + context => q|| + }, + + 'User help' => { + message => q|The user who will recieve an email when bucket processing is done.|, + lastUpdated => 0, + context => q|| + }, + + 'Begin analysis' => { + message => q|Begin analysis|, + lastUpdated => 0, + context => q|Button label to begin analyzing the logs.| + }, + +}; + +1; +#vim:ft=perl From 05591d82bd66c46f1018a6c7abdce4eaf2638adb Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Thu, 22 Jan 2009 14:05:30 +1100 Subject: [PATCH 04/31] Minor changes to sbin/installPassiveAnalytics.pl so that script would run. --- sbin/installPassiveAnalytics.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl index 4569fa992..9a8afd140 100644 --- a/sbin/installPassiveAnalytics.pl +++ b/sbin/installPassiveAnalytics.pl @@ -30,9 +30,9 @@ pod2usage( msg => "Must specify a config file!" ) unless $configFile; my $session = start( $webguiRoot, $configFile ); # Do your work here -#installLoggingTables($session); -#installPassiveAnalyticsRule($session); -#installPassiveAnalyticsConfig($session); +installLoggingTables($session); +installPassiveAnalyticsRule($session); +installPassiveAnalyticsConfig($session); installWorkflow($session); #addAdminGroup($session); @@ -159,7 +159,7 @@ sub installWorkflow { my $bucket = $workflow->addActivity('WebGUI::Workflow::Activity::BucketPassiveAnalytics'); $summarize->set('title', 'Perform duration analysis'); $bucket->set( 'title', 'Please log entries into buckets'); - $workflow->update({enabled => 1}); + $workflow->set({enabled => 1}); print "DONE!\n"; } From 3de4e4ac87a214b96bbfbb394e08fd4a8b1a9480 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 21 Jan 2009 22:14:10 -0800 Subject: [PATCH 05/31] Fix the name of the SummarizePassizeAnalytics workflow. --- lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm index 7d5e52f49..3f3c1fde1 100644 --- a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm @@ -45,7 +45,7 @@ sub definition { my $definition = shift; my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); push(@{$definition}, { - name=>$i18n->get('activityName'), + name=>$i18n->get('Summarize Passive Analytics'), properties=> { deltaInterval => { fieldType => 'interval', From 2bb63fa79b30844485bc2f0fc42c30111222bc56 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 22 Jan 2009 08:46:25 -0800 Subject: [PATCH 06/31] Log the complete URI, with query fragment. --- lib/WebGUI/Content/Asset.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebGUI/Content/Asset.pm b/lib/WebGUI/Content/Asset.pm index 64242302e..064ffe411 100644 --- a/lib/WebGUI/Content/Asset.pm +++ b/lib/WebGUI/Content/Asset.pm @@ -177,7 +177,7 @@ sub page { && substr($assetClass,0,4) ne 'File') { $session->db->write( q|INSERT INTO `passiveLog` (userId, sessionId, assetId, timestamp, url) VALUES (?,?,?,?,?)|, - [ $session->user->userId, $session->getId, $asset->getId, time(), $session->request->uri,] + [ $session->user->userId, $session->getId, $asset->getId, time(), $session->request->unparsed_uri,] ); } From efe62bae1e613d0f48ba436c23320ca6108e6acb Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 30 Jan 2009 08:40:00 -0800 Subject: [PATCH 07/31] Let the user choose which columns get sorted in a DataTable, js changes. --- www/extras/yui-webgui/build/form/datatable.js | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/www/extras/yui-webgui/build/form/datatable.js b/www/extras/yui-webgui/build/form/datatable.js index cedbf8ae9..a96db2181 100644 --- a/www/extras/yui-webgui/build/form/datatable.js +++ b/www/extras/yui-webgui/build/form/datatable.js @@ -338,7 +338,8 @@ WebGUI.Form.DataTable "help select row", "help add row", "help default sort", - "help reorder column" + "help reorder column", + "sortable" ] }, onpreload : { @@ -399,7 +400,8 @@ WebGUI.Form.DataTable div.parentNode.removeChild( div ); }; - var buttonLabel = this.i18n.get( "Form_DataTable", "delete column" ); + var sortableLabel = this.i18n.get( "Form_DataTable", "sortable" ); + var buttonLabel = this.i18n.get( "Form_DataTable", "delete column" ); var availableFormats = [ { "value" : "text", @@ -464,6 +466,16 @@ WebGUI.Form.DataTable format.appendChild( opt ); } div.appendChild( format ); + + div.appendChild( document.createTextNode(sortableLabel) ); + + var sorter = document.createElement( "input" ); + sorter.type = "checkbox"; + sorter.name = "canSort_" + i; + sorter.checked = cols[i].sortable; + sorter.value = 1; + div.appendChild( sorter ); + return div; }; @@ -475,6 +487,7 @@ WebGUI.Form.DataTable var newIdx = cols.length; // create a new column object cols[newIdx] = new YAHOO.widget.Column; + cols[newIdx].sortable = true; // add it to the dialog box this.appendChild( createTableColumn(newIdx,cols) ); }; @@ -600,9 +613,10 @@ WebGUI.Form.DataTable // Update columns var i = 0; while ( data[ "newKey_" + i ] ) { - var oldKey = data[ "oldKey_" + i ]; - var newKey = data[ "newKey_" + i ]; - var format = data[ "format_" + i ][0]; + var oldKey = data[ "oldKey_" + i ]; + var newKey = data[ "newKey_" + i ]; + var format = data[ "format_" + i ][0]; + var canSort = data[ "canSort_" + i ] var col = this.dataTable.getColumn( oldKey ); // Don't allow adding multiple columns with same key @@ -626,7 +640,7 @@ WebGUI.Form.DataTable key : newKey, formatter : format, resizeable : ( col ? col.resizeable : 1 ), - sortable : ( col ? col.sortable : 1 ) + sortable : canSort }; var newIndex = col ? col.getKeyIndex() : undefined; From bd38691cf092e7ad04884589a59a6891c8fafebf Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 30 Jan 2009 09:21:05 -0800 Subject: [PATCH 08/31] Perl code for user selectable columns in the data table. --- lib/WebGUI/Asset/Wobject/DataTable.pm | 1 + lib/WebGUI/Form/DataTable.pm | 7 ++++--- lib/WebGUI/i18n/English/Form_DataTable.pm | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/WebGUI/Asset/Wobject/DataTable.pm b/lib/WebGUI/Asset/Wobject/DataTable.pm index e8f25fd93..da18f460a 100644 --- a/lib/WebGUI/Asset/Wobject/DataTable.pm +++ b/lib/WebGUI/Asset/Wobject/DataTable.pm @@ -125,6 +125,7 @@ sub getDataTemplateVars { my $self = shift; my $json = $self->getDataJson; + $self->session->log->warn($json); my $dt = JSON->new->decode($json); # Make row data more friendly to templates diff --git a/lib/WebGUI/Form/DataTable.pm b/lib/WebGUI/Form/DataTable.pm index 3ebe5678a..a43a549c9 100644 --- a/lib/WebGUI/Form/DataTable.pm +++ b/lib/WebGUI/Form/DataTable.pm @@ -122,11 +122,11 @@ sub getDataTableHtml { # Not using a datastructure and JSON.pm because of function references for "parser" my $columnDef = '{' - . qq["key" : "$column->{ key }", ] - . qq["abbr" : "$column->{ key }", ] + . qq["key" : "$column->{ key }", ] + . qq["abbr" : "$column->{ key }", ] . qq["formatter" : "$column->{ formatter }", ] . qq["resizable" : 1, ] - . qq["sortable" : 1]; + . qq["sortable" : $column->{sortable} ]; # Automatically determine the parser to use if ( $parsers{ $column->{formatter} } ) { @@ -186,6 +186,7 @@ sub getDefaultValue { columns => [ { key => "New Column", formatter => "text", + sortable => "true", }, ], rows => [ { "New Column" => "Value", }, ], diff --git a/lib/WebGUI/i18n/English/Form_DataTable.pm b/lib/WebGUI/i18n/English/Form_DataTable.pm index ad51bad68..26e80a527 100644 --- a/lib/WebGUI/i18n/English/Form_DataTable.pm +++ b/lib/WebGUI/i18n/English/Form_DataTable.pm @@ -139,6 +139,13 @@ our $I18N = { lastUpdated => 0, context => q{Button to delete a column, shown in the Edit Schema dialog}, }, + + "sortable" => { + message => q{sortable}, + lastUpdated => 0, + context => q{Whether a column is sortable in the table}, + }, + }; 1; From f1968c9d40ee3d655fd3ac5fd9837f98a4f3ae23 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Sun, 1 Feb 2009 16:11:47 +1100 Subject: [PATCH 09/31] passiveLog entry should land in the first bucket that matches, not all buckets that match. Added help info about escaping meta-characters in regexps. --- lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm | 4 ++++ lib/WebGUI/i18n/English/PassiveAnalytics.pm | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index 170d50dfb..b66c2a806 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -107,7 +107,11 @@ EOSQL1 my $bucketFound = 0; RULE: foreach my $rule (@rules) { next RULE unless $rule->matchesBucket($entry); + + # Into the bucket she goes.. $bucketSth->execute([$entry->{userId}, $rule->get('bucketName'), $entry->{delta}, $entry->{stamp}]); + $bucketFound = 1; + last RULE; } if (!$bucketFound) { $bucketSth->execute([$entry->{userId}, 'Other', $entry->{delta}, $entry->{stamp}]); diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm index b46a12eca..205cc5f24 100644 --- a/lib/WebGUI/i18n/English/PassiveAnalytics.pm +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -52,6 +52,8 @@ $ = end of url
* = any amount
+ = 1 or more
? = 0 or 1
+Meta characters should be backslash-escaped if you want to match them as ordinary text, e.g.
+home\?func=match, or
|, lastUpdated => 0, context => q|| From 92841f2bf519eefca9db61a05957550ae791c625 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 6 Feb 2009 17:37:01 -0800 Subject: [PATCH 10/31] Must use stringy GUIDs to compare in Summarizer. --- lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm index 3f3c1fde1..b99ee1e2d 100644 --- a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm @@ -81,7 +81,7 @@ sub execute { my $endTime = time() + $self->getTTL; my $deltaInterval = $self->get('deltaInterval'); - my $passive = 'select * from passiveLog where userId <> 1 order by userId, sessionId, timeStamp'; + my $passive = q{select * from passiveLog where userId <> '1' order by userId, sessionId, timeStamp}; my $sth; my $lastUserId; my $lastSessionId; From aa6367d459d9fd7fda79e892064d7453c432e07a Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 6 Feb 2009 20:29:56 -0800 Subject: [PATCH 11/31] Bug fixes: sticky delta time and dropped 1st entry. Make the delta time interval sticky in the form where the workflow is launched. Also, fix a bug where the first line of data is dropped because the statement handle was read twice. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 2 +- lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 143a24b66..c7fbacb4f 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -110,7 +110,7 @@ sub www_editRuleflow { ); $f->integer( name => 'pauseInterval', - value => 300, + value => $session->form->get('pauseInterval') || 300, label => $i18n->get('pause interval'), hoverHelp => $i18n->get('pause interval help'), ); diff --git a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm index b99ee1e2d..ff98311a1 100644 --- a/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/SummarizePassiveAnalytics.pm @@ -101,7 +101,6 @@ sub execute { else { $sth = $session->db->read($passive); my $logLine = $sth->hashRef(); - $logLine = $sth->hashRef(); $lastUserId = $logLine->{userId}; $lastSessionId = $logLine->{sessionId}; $lastTimeStamp = $logLine->{timeStamp}; From 266dca5af641c537b6235c1584944731214b48af Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 13:36:52 -0800 Subject: [PATCH 12/31] Add admin settings for interval and table cleanup. Interval is used as the default in the editRuleflow page. table cleanup works as a setting, but doesn't do anything yet. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 63 ++++++++++++++++++++- lib/WebGUI/i18n/English/PassiveAnalytics.pm | 28 +++++++++ sbin/installPassiveAnalytics.pl | 12 ++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index c7fbacb4f..a1b9905bf 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -110,7 +110,7 @@ sub www_editRuleflow { ); $f->integer( name => 'pauseInterval', - value => $session->form->get('pauseInterval') || 300, + value => $session->form->get('pauseInterval') || $session->setting->get('passiveAnalyticsInterval') || 300, label => $i18n->get('pause interval'), hoverHelp => $i18n->get('pause interval help'), ); @@ -131,6 +131,7 @@ sub www_editRuleflow { } $steps .= ' Other
'; my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=settings'), $i18n->get('Passive Analytics Settings')); return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics'); } @@ -251,4 +252,64 @@ sub www_promoteRule { return www_editRuleflow($session); } +#------------------------------------------------------------------- + +=head2 www_settings ( session ) + +Configure Passive Analytics settings. + +=cut + +sub www_settings { + my $session = shift; + my $error = shift; + return $session->privilege->insufficient() unless canView($session); + if ($error) { + $error = qq|
$error
\n|; + } + my $i18n = WebGUI::International->new($session, "PassiveAnalytics"); + my $f = WebGUI::HTMLForm->new($session); + $f->hidden( + name=>'op', + value=>'passiveAnalytics' + ); + $f->hidden( + name=>'func', + value=>'settingsSave' + ); + $f->integer( + name => 'pauseInterval', + value => $session->form->get('pauseInterval') || $session->setting->get('passiveAnalyticsInterval') || 300, + label => $i18n->get('default pause interval'), + hoverHelp => $i18n->get('default pause interval help'), + ); + $f->yesNo( + name => 'deleteDelta', + value => $session->form->get('deleteDelta') || $session->setting->get('passiveAnalyticsDeleteDelta') || 0, + label => $i18n->get('Delete Delta Table?'), + hoverHelp => $i18n->get('Delete Delta Table? help'), + ); + $f->submit(); + my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=editRuleflow'), $i18n->get('Passive Analytics')); + return $ac->render($error.$f->print, 'Passive Analytics Settings'); +} + +#------------------------------------------------------------------- + +=head2 www_settingsSave ( session ) + +Save Passive Analytics settings. + +=cut + +sub www_settingsSave { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $form = $session->form; + $session->setting->set('passiveAnalyticsInterval', $form->process('pauseInterval', 'integer')); + $session->setting->set('passiveAnalyticsDeleteDelta', $form->process('deleteDelta', 'yesNo' )); + return www_settings($session); +} + 1; diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm index 205cc5f24..98a99bc41 100644 --- a/lib/WebGUI/i18n/English/PassiveAnalytics.pm +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -20,6 +20,16 @@ our $I18N = { lastUpdated => 0, }, + 'default pause interval' => { + message => q|Default Pause Threshold|, + lastUpdated => 0, + }, + + 'default pause interval help' => { + message => q|Set the default pause interval displayed the user sees in the Passive Analytics screen.|, + lastUpdated => 0, + }, + 'other' => { message => q|Other|, lastUpdated => 0, @@ -65,6 +75,12 @@ home\?func=match, or
context => q|| }, + 'Passive Analytics Settings' => { + message => q|Passive Analytics Settings|, + lastUpdated => 0, + context => q|| + }, + 'Edit Rule' => { message => q|Edit Rule|, lastUpdated => 0, @@ -95,6 +111,18 @@ home\?func=match, or
context => q|Button label to begin analyzing the logs.| }, + 'Delete Delta Table?' => { + message => q|Delete Delta Table?|, + lastUpdated => 0, + context => q|Button label to begin analyzing the logs.| + }, + + 'Delete Delta Table? help' => { + message => q|Should the delta table be cleaned up after the Passive Analytics analyzer is done?|, + lastUpdated => 0, + context => q|Button label to begin analyzing the logs.| + }, + }; 1; diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl index 9a8afd140..45398e8f3 100644 --- a/sbin/installPassiveAnalytics.pl +++ b/sbin/installPassiveAnalytics.pl @@ -35,6 +35,7 @@ installPassiveAnalyticsRule($session); installPassiveAnalyticsConfig($session); installWorkflow($session); #addAdminGroup($session); +addPassiveAnalyticsSettings($session); finish($session); @@ -93,6 +94,17 @@ sub installPassiveAnalyticsRule { print "DONE!\n"; } +#---------------------------------------------------------------------------- +# Add the PassiveAnalytics Rule table +sub addPassiveAnalyticsSettings { + my $session = shift; + print "\tInstall Passive Analytics settings... "; + # and here's our code + $session->setting->add('passiveAnalyticsInterval', 300); + $session->setting->add('passiveAnalyticsDeleteDelta', 0); + print "DONE!\n"; +} + #---------------------------------------------------------------------------- # Add the Passive Analytics config file entry # for the adminConsole and the content handler From fa9a7ca87f3ca64f274f654f3668650c1ac5e6f9 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 13:55:04 -0800 Subject: [PATCH 13/31] Bucket activity now clears deltaLog based on settings. It also logs that the log was deleted, and adds that to the email that is sent out. --- lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index b66c2a806..42dd8ea1d 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -126,12 +126,18 @@ EOSQL1 $instance->setScratch('logIndex', $logIndex); return $self->WAITING(1); } + my $message = 'Passive analytics is done.'; + if ($session->setting->get('passiveAnalyticsDeleteDelta')) { + $session->log->info('Clearing Passive Analytics delta log'); + $session->db->write('delete from deltaLog'); + $message .= ' The delta log has been cleaned up.'; + } my $inbox = WebGUI::Inbox->new($self->session); $inbox->addMessage({ status => 'unread', subject => 'Passive analytics is done', userId => $self->get('userId'), - message => 'Passive analytics is done', + message => $message, }); return $self->COMPLETE; From 338c828a884bdae711906d84501df56c44fc0f9d Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 15:51:08 -0800 Subject: [PATCH 14/31] Patch HTMLForm to make dynamically generated forms sticky. --- lib/WebGUI/HTMLForm.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/WebGUI/HTMLForm.pm b/lib/WebGUI/HTMLForm.pm index 683f21e83..47decd4eb 100644 --- a/lib/WebGUI/HTMLForm.pm +++ b/lib/WebGUI/HTMLForm.pm @@ -155,6 +155,7 @@ a C method to provide that information. sub dynamicForm { my ($self, $formDefinition, $fieldList, $parent) = @_; + my $form = $self->session->form; foreach my $definition (reverse @{$formDefinition}) { my $properties = $definition->{$fieldList}; foreach my $fieldname (keys %{$properties}) { @@ -165,7 +166,7 @@ sub dynamicForm { $params{$key} = $formDefinition->[0]{name}; } } - $params{value} = $parent->get($fieldname); + $params{value} = $form->get($fieldname) || $parent->get($fieldname); $params{name} = $fieldname; $self->dynamicField(%params); } From 9a5127fb7ef059a257ffc87fe35bd3856c276c18 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 15:53:40 -0800 Subject: [PATCH 15/31] Basic regexp checking for submitted rules. Checks the rules as they are entered or edited, and provides feedback from perl about any errors. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index a1b9905bf..291233ca7 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -175,9 +175,12 @@ Displays a form to edit the properties rule. =cut sub www_editRule { - my $session = shift; + my ($session, $error) = @_; return $session->privilege->insufficient() unless canView($session); + if ($error) { + $error = qq|
$error
\n|; + } ##Make a PassiveAnalytics rule to use to populate the form. my $ruleId = $session->form->get('ruleId'); my $rule; @@ -204,7 +207,7 @@ sub www_editRule { if ($ruleId eq 'new') { $rule->delete; } - return $ac->render($form->print,$i18n->get('Edit Rule')); + return $ac->render($error.$form->print,$i18n->get('Edit Rule')); } #------------------------------------------------------------------- @@ -217,8 +220,19 @@ Saves the results of www_editRule(). sub www_editRuleSave { my $session = shift; + my $form = $session->form; return $session->privilege->insufficient() unless canView($session); - my $ruleId = $session->form->get('ruleId'); + my $regexp = $form->get('regexp'); + eval { + 'fooBarBaz' =~ qr/$regexp/; + }; + if ($@) { + $session->log->warn("Error: $@"); + my $error = $@; + $error =~ s/at \S+?\.pm line \d+.*$//; + return www_editRule($session, $error); + } + my $ruleId = $form->get('ruleId'); my $rule; if ($ruleId eq 'new') { $rule = WebGUI::PassiveAnalytics::Rule->create($session, {}); From c0adc273aa2329ed3f6ab9e259802f29e72eca84 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 18:16:26 -0800 Subject: [PATCH 16/31] Label for regular expression errors on rule save. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 2 ++ lib/WebGUI/i18n/English/PassiveAnalytics.pm | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 291233ca7..622cb8c68 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -230,6 +230,8 @@ sub www_editRuleSave { $session->log->warn("Error: $@"); my $error = $@; $error =~ s/at \S+?\.pm line \d+.*$//; + my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); + $error = join ' ', $i18n->get('Regular Expression Error:'), $error; return www_editRule($session, $error); } my $ruleId = $form->get('ruleId'); diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm index 98a99bc41..01747ca57 100644 --- a/lib/WebGUI/i18n/English/PassiveAnalytics.pm +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -123,6 +123,12 @@ home\?func=match, or
context => q|Button label to begin analyzing the logs.| }, + 'Regular Expression Error:' => { + message => q|Regular Expression Error:|, + lastUpdated => 0, + context => q|Error displayed when a user enters in a bad regular expression. This label will be followed by the error from perl.| + }, + }; 1; From 28d2c551c364593c1c09811062654f371dc9d60b Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 18:52:09 -0800 Subject: [PATCH 17/31] Passive Analytics Workflow status checks. If the workflow is active, do not show the submit button in the Ruleflow form. And show a message in the form. And no sneaky paths directly into the RuleflowSave, either. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 622cb8c68..25fd8ddc4 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -25,6 +25,19 @@ running them. #---------------------------------------------------------------------------- +=head2 analysisActive ( session ) + +Returns true if an instance of the PassiveAnalytics workflow is active. + +=cut + +sub analysisActive { + my $session = shift; + return $session->db->quickScalar(q!select count(*) from WorkflowInstance where workflowId='PassiveAnalytics000001'!); +} + +#---------------------------------------------------------------------------- + =head2 canView ( session [, user] ) Returns true if the user can administrate this operation. user defaults to @@ -114,7 +127,12 @@ sub www_editRuleflow { label => $i18n->get('pause interval'), hoverHelp => $i18n->get('pause interval help'), ); - $f->submit(value => $i18n->get('Begin analysis')); + if (analysisActive($session)) { + $f->raw('Passive Analytics analysis is currently active'); + } + else { + $f->submit(value => $i18n->get('Begin analysis')); + } my $steps = ''; my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session); my $icon = $session->icon; @@ -146,6 +164,8 @@ Saves the results of www_editRuleflow() sub www_editRuleflowSave { my $session = shift; return $session->privilege->insufficient() unless canView($session); + return www_editRuleflow($session, 'Passive Analytics is already active. Please do not try to subvert the UI in the future') + if analysisActive($session); my $workflow = WebGUI::Workflow->new($session, 'PassiveAnalytics000001'); return www_editRuleflow($session, "The Passive Analytics workflow has been deleted. Please contact an Administrator immediately.") unless defined $workflow; my $delta = $session->form->process('pauseInterval','integer'); From b206ad127054e504d54324aa09c2bd73a54e783d Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 9 Feb 2009 19:07:02 -0800 Subject: [PATCH 18/31] Tweak the display a little more to make it nice. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 25fd8ddc4..159483b8d 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -128,7 +128,7 @@ sub www_editRuleflow { hoverHelp => $i18n->get('pause interval help'), ); if (analysisActive($session)) { - $f->raw('Passive Analytics analysis is currently active'); + $f->raw(''); } else { $f->submit(value => $i18n->get('Begin analysis')); @@ -182,7 +182,7 @@ sub www_editRuleflowSave { return www_editRuleflow($session, "Error creating the workflow instance."); } $instance->start('skipRealtime'); - return www_editRuleflow($session, "Passive Analytics session started"); + return www_editRuleflow($session); } From 0b0962096e1a4609176a8c34f84c0e985a43e98d Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Tue, 10 Feb 2009 18:39:10 -0800 Subject: [PATCH 19/31] Real PA status, as described in the RFE. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 19 +++++++++++---- .../Activity/BucketPassiveAnalytics.pm | 2 ++ sbin/installPassiveAnalytics.pl | 24 ++++++++++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 159483b8d..02e7fed8c 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -11,6 +11,7 @@ use WebGUI::Utility; use WebGUI::HTMLForm; use WebGUI::Workflow; use WebGUI::Workflow::Instance; +use WebGUI::User; =head1 NAME @@ -33,7 +34,11 @@ Returns true if an instance of the PassiveAnalytics workflow is active. sub analysisActive { my $session = shift; - return $session->db->quickScalar(q!select count(*) from WorkflowInstance where workflowId='PassiveAnalytics000001'!); + my ($running, $startDate, $endDate, $userId) = $session->db->quickArray(q!select running, startDate, endDate, userId from passiveAnalyticsStatus!); + if (wantarray) { + return $running, $startDate, $endDate, WebGUI::User->new($session, $userId); + } + return $running; } #---------------------------------------------------------------------------- @@ -103,9 +108,13 @@ sub www_editRuleflow { my $session = shift; my $error = shift; return $session->privilege->insufficient() unless canView($session); + my ($running, $startDate, $endDate, $user) = analysisActive($session); if ($error) { $error = qq|
$error
\n|; } + elsif (!$running) { + $error = qq|
Passive Analytics analysis completed on $endDate
\n|; + } my $i18n = WebGUI::International->new($session, "PassiveAnalytics"); my $addmenu = '
'; $addmenu .= sprintf '%s', @@ -127,8 +136,10 @@ sub www_editRuleflow { label => $i18n->get('pause interval'), hoverHelp => $i18n->get('pause interval help'), ); - if (analysisActive($session)) { - $f->raw('
'); + if ($running) { + $f->raw(sprintf <username); + +EOD } else { $f->submit(value => $i18n->get('Begin analysis')); @@ -182,6 +193,7 @@ sub www_editRuleflowSave { return www_editRuleflow($session, "Error creating the workflow instance."); } $instance->start('skipRealtime'); + $session->db->write('update passiveAnalyticsStatus set startDate=NOW(), userId=?, endDate=?, running=1', [$session->user->userId, '']); return www_editRuleflow($session); } @@ -247,7 +259,6 @@ sub www_editRuleSave { 'fooBarBaz' =~ qr/$regexp/; }; if ($@) { - $session->log->warn("Error: $@"); my $error = $@; $error =~ s/at \S+?\.pm line \d+.*$//; my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index 42dd8ea1d..712e70fa3 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -74,6 +74,7 @@ Analyze the deltaLog table, and generate the bucketLog table. sub execute { my ($self, undef, $instance) = @_; my $session = $self->session; + sleep 45; my $endTime = time() + $self->getTTL; my $expired = 0; @@ -139,6 +140,7 @@ EOSQL1 userId => $self->get('userId'), message => $message, }); + $session->db->write('update passiveAnalyticsStatus set endDate=NOW(), running=0'); return $self->COMPLETE; } diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl index 45398e8f3..82c819a35 100644 --- a/sbin/installPassiveAnalytics.pl +++ b/sbin/installPassiveAnalytics.pl @@ -34,8 +34,8 @@ installLoggingTables($session); installPassiveAnalyticsRule($session); installPassiveAnalyticsConfig($session); installWorkflow($session); -#addAdminGroup($session); addPassiveAnalyticsSettings($session); +addPassiveAnalyticsStatus($session); finish($session); @@ -105,6 +105,28 @@ sub addPassiveAnalyticsSettings { print "DONE!\n"; } +#---------------------------------------------------------------------------- +# Add the PassiveAnalytics Rule table +sub addPassiveAnalyticsStatus { + my $session = shift; + my $db = $session->db; + print "\tInstall Passive Analytics status table... "; + # and here's our code + $db->write(<write(<write('insert into passiveAnalyticsStatus (userId) VALUES (3)'); + print "DONE!\n"; +} + #---------------------------------------------------------------------------- # Add the Passive Analytics config file entry # for the adminConsole and the content handler From c5bf65fdd846844d9d3fc072a84fc1ea59411916 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 11 Feb 2009 09:40:10 -0800 Subject: [PATCH 20/31] Remove 45 second sleep used for testing. --- lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index 712e70fa3..b0ade9389 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -74,7 +74,6 @@ Analyze the deltaLog table, and generate the bucketLog table. sub execute { my ($self, undef, $instance) = @_; my $session = $self->session; - sleep 45; my $endTime = time() + $self->getTTL; my $expired = 0; From f676bfc53688881d87eb4af06ae90b9426d3484a Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 11 Feb 2009 10:57:19 -0800 Subject: [PATCH 21/31] Provide CVS data for all logs. Generalized the CSV creation routine, then made wrappers for all log types. --- lib/WebGUI/PassiveAnalytics/Flow.pm | 89 +++++++++++++++++++++ lib/WebGUI/i18n/English/PassiveAnalytics.pm | 18 +++++ 2 files changed, 107 insertions(+) diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 02e7fed8c..10a4c33ea 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -12,6 +12,7 @@ use WebGUI::HTMLForm; use WebGUI::Workflow; use WebGUI::Workflow::Instance; use WebGUI::User; +use WebGUI::Text; =head1 NAME @@ -56,6 +57,44 @@ sub canView { return $user->isInGroup( 3 ); } +#---------------------------------------------------------------------------- + +=head2 exportSomething ( session, sth, filename ) + +Generates CSV data from the supplied statement handle and generates +a temporary WebGUI::Storage object containing that data in the requested +filename. + +This subroutine also does a setRedirect to the URL of the file in +the storage object. + +=head3 session + +Session variable, to set the http redirect correctly. + +=head3 sth + +Statement handle for reading data and getting column names + +=head3 filename + +The name of the file to create inside the storage object. + +=cut + +sub exportSomething { + my ($session, $sth, $filename) = @_; + my $storage = WebGUI::Storage->createTemp($session); + my @columns = $sth->getColumnNames; + my $csvData = WebGUI::Text::joinCSV( @columns ). "\n"; + while (my $row = $sth->hashRef()) { + my @row = @{ $row }{@columns}; + $csvData .= WebGUI::Text::joinCSV(@row) . "\n"; + } + $storage->addFileFromScalar($filename, $csvData); + $session->http->setRedirect($storage->getUrl($filename)); +} + #------------------------------------------------------------------- =head2 www_deleteRule ( ) @@ -161,6 +200,11 @@ EOD $steps .= '
Passive Analytics analysis is currently active
Passive Analytics analysis is currently active
Passive Analytics analysis is currently active. Analysis was begun at %s by %s
 Other
'; my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=settings'), $i18n->get('Passive Analytics Settings')); + if (!$running) { + $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=exportBucketData'), $i18n->get('Export bucket data')); + $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=exportDeltaData'), $i18n->get('Export delta data')); + $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=exportLogs'), $i18n->get('Export raw logs')); + } return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics'); } @@ -277,6 +321,51 @@ sub www_editRuleSave { return www_editRuleflow($session); } +#------------------------------------------------------------------- + +=head2 www_exportBucketData ( ) + +Dump the contents of the bucket log. + +=cut + +sub www_exportBucketData { + my ($session) = @_; + my $bucket = $session->db->read('select * from bucketLog order by userId, Bucket, timeStamp'); + exportSomething($session, $bucket, 'bucketData.csv'); + return "redirect"; +} + +#------------------------------------------------------------------- + +=head2 www_exportDeltaData ( ) + +Dump the contents of the delta log. + +=cut + +sub www_exportDeltaData { + my ($session) = @_; + my $delta = $session->db->read('select * from deltaLog order by userId, timeStamp'); + exportSomething($session, $delta, 'deltaData.csv'); + return "redirect"; +} + +#------------------------------------------------------------------- + +=head2 www_exportLogs ( ) + +Dump the contents of the raw log. + +=cut + +sub www_exportLogs { + my ($session) = @_; + my $raw = $session->db->read('select * from passiveLog order by userId, timeStamp'); + exportSomething($session, $raw, 'passiveData.csv'); + return "redirect"; +} + #------------------------------------------------------------------ =head2 www_promoteRule ( session ) diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm index 01747ca57..1a0e9367e 100644 --- a/lib/WebGUI/i18n/English/PassiveAnalytics.pm +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -129,6 +129,24 @@ home\?func=match, or
context => q|Error displayed when a user enters in a bad regular expression. This label will be followed by the error from perl.| }, + 'Export bucket data' => { + message => q|Export bucket data|, + lastUpdated => 0, + context => q|URL label to export data in CSV format|, + }, + + 'Export delta data' => { + message => q|Export delta data|, + lastUpdated => 0, + context => q|URL label to export data in CSV format|, + }, + + 'Export raw logs' => { + message => q|Export raw logs|, + lastUpdated => 0, + context => q|URL label to raw log data in CSV format|, + }, + }; 1; From 4df4862c681c2090c28ad34d89dd828883f476bc Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 11 Feb 2009 17:11:24 -0800 Subject: [PATCH 22/31] Add an enable button, to disable Logging. Refactor out logging code into its own module for cleanliness. --- lib/WebGUI/Content/Asset.pm | 11 ++--------- lib/WebGUI/PassiveAnalytics/Flow.pm | 7 +++++++ lib/WebGUI/i18n/English/PassiveAnalytics.pm | 12 ++++++++++++ sbin/installPassiveAnalytics.pl | 3 ++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/WebGUI/Content/Asset.pm b/lib/WebGUI/Content/Asset.pm index 064ffe411..64480777a 100644 --- a/lib/WebGUI/Content/Asset.pm +++ b/lib/WebGUI/Content/Asset.pm @@ -18,6 +18,7 @@ use strict; use LWP::MediaTypes qw(guess_media_type); use Time::HiRes; use WebGUI::Asset; +use WebGUI::PassiveAnalytics::Logging; use Apache2::Const -compile => qw(OK); @@ -171,15 +172,7 @@ sub page { } } ##Passive Analytics Logging - my $assetClass = $asset->get('className'); - $assetClass =~ s/^WebGUI::Asset:://; - if ( $assetClass ne 'Snippet' - && substr($assetClass,0,4) ne 'File') { - $session->db->write( - q|INSERT INTO `passiveLog` (userId, sessionId, assetId, timestamp, url) VALUES (?,?,?,?,?)|, - [ $session->user->userId, $session->getId, $asset->getId, time(), $session->request->unparsed_uri,] - ); - } + WebGUI::PassiveAnalytics::Logging::log($session, $asset); $output = tryAssetMethod($session,$asset,$method); $output = tryAssetMethod($session,$asset,"view") unless ($output || ($method eq "view")); diff --git a/lib/WebGUI/PassiveAnalytics/Flow.pm b/lib/WebGUI/PassiveAnalytics/Flow.pm index 10a4c33ea..6c9538d77 100644 --- a/lib/WebGUI/PassiveAnalytics/Flow.pm +++ b/lib/WebGUI/PassiveAnalytics/Flow.pm @@ -425,6 +425,12 @@ sub www_settings { label => $i18n->get('Delete Delta Table?'), hoverHelp => $i18n->get('Delete Delta Table? help'), ); + $f->yesNo( + name => 'enabled', + value => $session->form->get('enabled') || $session->setting->get('passiveAnalyticsEnabled') || 0, + label => $i18n->get('Enabled?'), + hoverHelp => $i18n->get('Enabled? help'), + ); $f->submit(); my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); $ac->addSubmenuItem($session->url->page('op=passiveAnalytics;func=editRuleflow'), $i18n->get('Passive Analytics')); @@ -445,6 +451,7 @@ sub www_settingsSave { my $form = $session->form; $session->setting->set('passiveAnalyticsInterval', $form->process('pauseInterval', 'integer')); $session->setting->set('passiveAnalyticsDeleteDelta', $form->process('deleteDelta', 'yesNo' )); + $session->setting->set('passiveAnalyticsEnabled', $form->process('enabled', 'yesNo' )); return www_settings($session); } diff --git a/lib/WebGUI/i18n/English/PassiveAnalytics.pm b/lib/WebGUI/i18n/English/PassiveAnalytics.pm index 1a0e9367e..4cca51390 100644 --- a/lib/WebGUI/i18n/English/PassiveAnalytics.pm +++ b/lib/WebGUI/i18n/English/PassiveAnalytics.pm @@ -123,6 +123,18 @@ home\?func=match, or
context => q|Button label to begin analyzing the logs.| }, + 'Enabled?' => { + message => q|Enable Passive Analytics?|, + lastUpdated => 0, + context => q|| + }, + + 'Enabled? help' => { + message => q|Passive Analytics will do no logging until enabled.|, + lastUpdated => 0, + context => q|| + }, + 'Regular Expression Error:' => { message => q|Regular Expression Error:|, lastUpdated => 0, diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl index 82c819a35..cad304e45 100644 --- a/sbin/installPassiveAnalytics.pl +++ b/sbin/installPassiveAnalytics.pl @@ -95,13 +95,14 @@ sub installPassiveAnalyticsRule { } #---------------------------------------------------------------------------- -# Add the PassiveAnalytics Rule table +# Add the PassiveAnalytics Settings sub addPassiveAnalyticsSettings { my $session = shift; print "\tInstall Passive Analytics settings... "; # and here's our code $session->setting->add('passiveAnalyticsInterval', 300); $session->setting->add('passiveAnalyticsDeleteDelta', 0); + $session->setting->add('passiveAnalyticsEnabled', 0); print "DONE!\n"; } From 1fe9039060f9360dcf8914ca97d504276c51f83a Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 11 Feb 2009 17:11:57 -0800 Subject: [PATCH 23/31] And add the logging class. --- lib/WebGUI/PassiveAnalytics/Logging.pm | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/WebGUI/PassiveAnalytics/Logging.pm diff --git a/lib/WebGUI/PassiveAnalytics/Logging.pm b/lib/WebGUI/PassiveAnalytics/Logging.pm new file mode 100644 index 000000000..26f5bd3d8 --- /dev/null +++ b/lib/WebGUI/PassiveAnalytics/Logging.pm @@ -0,0 +1,47 @@ +package WebGUI::PassiveAnalytics::Logging; + +use strict; +use WebGUI::Session; +use WebGUI::Asset; + +=head1 NAME + +Package WebGUI::PassiveAnalytics::Logging + +=head1 DESCRIPTION + +Encapsulate all logging functions in here. + +=cut + +#---------------------------------------------------------------------------- + +=head2 log ( session, asset ) + +Log Passive Analytics data to the db. + +=head3 session + +A session variable. + +=head3 asset + +The asset to log. + +=cut + +sub log { + my ($session, $asset) = @_; + return unless $session->setting->get('passiveAnalyticsEnabled'); + my $assetClass = $asset->get('className'); + $assetClass =~ s/^WebGUI::Asset:://; + if ( $assetClass ne 'Snippet' + && substr($assetClass,0,4) ne 'File') { + $session->db->write( + q|INSERT INTO `passiveLog` (userId, sessionId, assetId, timestamp, url) VALUES (?,?,?,?,?)|, + [ $session->user->userId, $session->getId, $asset->getId, time(), $session->request->unparsed_uri,] + ); + } +} + +1; From a0623c84ab9d5b9c53dabaea3d38032099148e19 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 13 Feb 2009 08:14:39 -0800 Subject: [PATCH 24/31] If userId=0, then do not send emails when the workflow is done. --- .../Workflow/Activity/BucketPassiveAnalytics.pm | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index b0ade9389..1e48765fc 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -132,13 +132,16 @@ EOSQL1 $session->db->write('delete from deltaLog'); $message .= ' The delta log has been cleaned up.'; } - my $inbox = WebGUI::Inbox->new($self->session); - $inbox->addMessage({ - status => 'unread', - subject => 'Passive analytics is done', - userId => $self->get('userId'), - message => $message, - }); + ##If userId was set to 0, do not send any emails. + if ($self->get('userId')) { + my $inbox = WebGUI::Inbox->new($self->session); + $inbox->addMessage({ + status => 'unread', + subject => 'Passive analytics is done', + userId => $self->get('userId'), + message => $message, + }); + } $session->db->write('update passiveAnalyticsStatus set endDate=NOW(), running=0'); return $self->COMPLETE; From 3bafce6e3e760687a0ec87db0eb7f9a50aa72c9a Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 13 Feb 2009 08:15:14 -0800 Subject: [PATCH 25/31] Test for running benchmarking. --- t/Workflow/Activity/BucketPassiveAnalytics.t | 106 +++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 t/Workflow/Activity/BucketPassiveAnalytics.t diff --git a/t/Workflow/Activity/BucketPassiveAnalytics.t b/t/Workflow/Activity/BucketPassiveAnalytics.t new file mode 100644 index 000000000..e7d437acc --- /dev/null +++ b/t/Workflow/Activity/BucketPassiveAnalytics.t @@ -0,0 +1,106 @@ + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../lib"; +#use DB; + +use WebGUI::Test; +use WebGUI::Asset; +use WebGUI::PassiveAnalytics::Rule; +use WebGUI::Workflow::Activity::BucketPassiveAnalytics; +use WebGUI::Text; + +use Test::More; + +plan tests => 1; # increment this value for each test you create + +my $session = WebGUI::Test->session; +$session->user({userId => 3}); + +my $workflow = WebGUI::Workflow->new($session, 'PassiveAnalytics000001'); +my $activities = $workflow->getActivities(); +##Note, they're in order, and the order is known. +$activities->[0]->set('deltaInterval', 100); +$activities->[1]->set('userId', 0); ##To disable sending emails +diag "Configured activities"; + +my $instance = WebGUI::Workflow::Instance->create($session, + { + workflowId => $workflow->getId, + skipSpectreNotification => 1, + priority => 1, + } +); +##Rule label, url, and regexp +my @ruleSets = ( + ['home', '/home', '^\/home' ], + ['one', '/one', '^\/one$' ], + ['two', '/two', '^\/two$' ], + ['three', '/three', '^\/three$' ], + ['end', '/blah/blah/end', 'end$' ], + ['casa', '/home/casa', 'casa$' ], + ['uno', '/one/uno', 'uno$' ], + ['dos', '/two/dos', 'dos$' ], + ['tres', '/three/tres', 'tres$' ], + ['alpha', '/alpha/aee', '.alpha.aee' ], + ['beta', '/beta/bee', '.beta.bee' ], + ['gamma', '/gamma/cee', '.gamma.cee' ], + ['delta', '/delta/dee', '.delta.dee' ], + ['eee', '/epsilon/eee', 'eee$' ], + ['thingy1', '/thingy?thingId=1', '^.thingy\?thingId=1' ], + ['rogerRoger', '/roger/roger', '(?:\/roger){2}' ], + ['roger', '/roger', '^\/roger' ], + ['thingy2', '/thingy?thingId=2', '^.thingy\?thingId=2' ], + ['beet', '/beta/beet', '.beta.beet' ], + ['zero', '/yelnats', 'yelnats' ], +); + +my @url2 = @ruleSets; +diag "Making rules"; +while (my $spec = shift @url2) { + my ($bucket, undef, $regexp) = @{ $spec }; + WebGUI::PassiveAnalytics::Rule->create($session, { bucketName => $bucket, regexp => $regexp }); +} + +my @urls = map {$_->[1]} @ruleSets; +diag "Making log data with " . scalar @urls . " urls"; +loadLogData($session, @urls); +diag "Data logged"; + +##Build rulesets + +##Now, run it and wait for it to finish +my $counter = 0; +diag time(); +PAUSE: while (my $retval = $instance->run()) { + diag $retval; + last PAUSE if $retval eq 'done'; + last PAUSE if $counter++ >= 16; +} +diag time(); + +ok(1, 'One test'); + +END { + $session->db->write('delete from passiveLog'); + $session->db->write('delete from analyticRule'); + $instance->delete; +} + +sub loadLogData { + my ($session, @urls) = @_; + $session->db->write('delete from passiveLog'); + my $insert = $session->db->prepare( + q!insert into passiveLog (userId, sessionId, timeStamp, url, assetId) VALUES (?,?,?,?,'assetId')! + ); + my $logCount = 5000; + my $counter; + my $startTime = 1000; + my $numUrls = scalar @urls; + while ($counter++ < $logCount) { + my $index = int rand($numUrls); + my $url = $urls[$index]; + $insert->execute([2, 25, $startTime, $url]); + $startTime += int(rand(10))+1; + } +} From 30d171515ad57a3bbe076916ee32d60206930b76 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 16 Feb 2009 13:04:33 -0800 Subject: [PATCH 26/31] Optimize the Bucket analysis for speed. Optimized in 3 ways. 1) Local caching and compiling of all regexps. 2) URL level bucket caching 3) Tweaked canShowDebug to speed it up. This was ported back to the core. --- .../Activity/BucketPassiveAnalytics.pm | 30 ++++++++++++------- t/Workflow/Activity/BucketPassiveAnalytics.t | 4 ++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm index 1e48765fc..a83acbc26 100644 --- a/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm +++ b/lib/WebGUI/Workflow/Activity/BucketPassiveAnalytics.pm @@ -81,7 +81,8 @@ sub execute { my @rules = (); my $getARule = WebGUI::PassiveAnalytics::Rule->getAllIterator($session); while (my $rule = $getARule->()) { - push @rules, $rule; + my $regexp = $rule->get('regexp'); + push @rules, [ $rule->get('bucketName'), qr/$regexp/]; } ##Get the index stored from the last invocation of the Activity. If this is @@ -90,6 +91,7 @@ sub execute { if ($logIndex == 0) { $session->db->write('delete from bucketLog'); } + my %bucketCache = (); ##Configure all the SQL my $deltaSql = <<"EOSQL1"; @@ -105,16 +107,24 @@ EOSQL1 DELTA_ENTRY: while (my $entry = $deltaSth->hashRef()) { ++$logIndex; my $bucketFound = 0; - RULE: foreach my $rule (@rules) { - next RULE unless $rule->matchesBucket($entry); - - # Into the bucket she goes.. - $bucketSth->execute([$entry->{userId}, $rule->get('bucketName'), $entry->{delta}, $entry->{stamp}]); - $bucketFound = 1; - last RULE; + my $url = $entry->{url}; + if (exists $bucketCache{$url}) { + $bucketSth->execute([$entry->{userId}, $bucketCache{$url}, $entry->{delta}, $entry->{stamp}]); } - if (!$bucketFound) { - $bucketSth->execute([$entry->{userId}, 'Other', $entry->{delta}, $entry->{stamp}]); + else { + RULE: foreach my $rule (@rules) { + next RULE unless $url =~ $rule->[1]; + + # Into the bucket she goes.. + $bucketCache{$url} = $rule->[0]; + $bucketSth->execute([$entry->{userId}, $rule->[0], $entry->{delta}, $entry->{stamp}]); + $bucketFound = 1; + last RULE; + } + if (!$bucketFound) { + $bucketCache{$url} = 'Other'; + $bucketSth->execute([$entry->{userId}, 'Other', $entry->{delta}, $entry->{stamp}]); + } } if (time() > $endTime) { $expired = 1; diff --git a/t/Workflow/Activity/BucketPassiveAnalytics.t b/t/Workflow/Activity/BucketPassiveAnalytics.t index e7d437acc..b05ab5cb4 100644 --- a/t/Workflow/Activity/BucketPassiveAnalytics.t +++ b/t/Workflow/Activity/BucketPassiveAnalytics.t @@ -72,11 +72,13 @@ diag "Data logged"; ##Now, run it and wait for it to finish my $counter = 0; diag time(); +#DB::enable_profile(); PAUSE: while (my $retval = $instance->run()) { diag $retval; last PAUSE if $retval eq 'done'; last PAUSE if $counter++ >= 16; } +#DB::disable_profile(); diag time(); ok(1, 'One test'); @@ -93,7 +95,7 @@ sub loadLogData { my $insert = $session->db->prepare( q!insert into passiveLog (userId, sessionId, timeStamp, url, assetId) VALUES (?,?,?,?,'assetId')! ); - my $logCount = 5000; + my $logCount = 15000; my $counter; my $startTime = 1000; my $numUrls = scalar @urls; From 38324ada22feb45dbf5738addc7e14ab1aedc565 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Wed, 4 Mar 2009 12:43:24 +1100 Subject: [PATCH 27/31] Added some screenshots --- PassiveAnalytics_main.png | Bin 0 -> 42541 bytes PassiveAnalytics_settings.png | Bin 0 -> 29298 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 PassiveAnalytics_main.png create mode 100644 PassiveAnalytics_settings.png diff --git a/PassiveAnalytics_main.png b/PassiveAnalytics_main.png new file mode 100644 index 0000000000000000000000000000000000000000..9ee8931796e442166ddcc862e4f490ed0fc09753 GIT binary patch literal 42541 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 Q5x{}ItfThgl6A=c0DV%#(EtDd literal 0 HcmV?d00001 diff --git a/PassiveAnalytics_settings.png b/PassiveAnalytics_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..0bdee2ef44c309432aa802d436d43d09368220eb GIT binary patch literal 29298 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%0AZw7ouQc(t{q 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 literal 0 HcmV?d00001 From dcfead6fce9b72644459fc8c75c283f0f19b9f35 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Wed, 4 Mar 2009 13:39:44 +1100 Subject: [PATCH 28/31] Added PASSIVE_ANALYTICS_README.txt --- PASSIVE_ANALYTICS_README.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 PASSIVE_ANALYTICS_README.txt diff --git a/PASSIVE_ANALYTICS_README.txt b/PASSIVE_ANALYTICS_README.txt new file mode 100644 index 000000000..638adc202 --- /dev/null +++ b/PASSIVE_ANALYTICS_README.txt @@ -0,0 +1,17 @@ +To try out Passive Analytics, run: + + sbin/installPassiveAnalytics.pl + +or even better: + + wgd reset --dev --util=sbin/installPassiveAnalytics.pl + +Quick Tour +* Go into the Passive Analytics section of the Admin Console +* Create some buckets +* Click around the site (and watch the passiveLog table get populated) +* Click "Begin Analysis" +* Wait for an email, or refresh to see the running status +* Download the Bucket Data, Delta Data and/or Raw Logs and view in a spreadsheet program + +For more info, see: http://www.plainblack.com/rfe/request-for-enhancement/8862 From 13fe8004bc55274c4cacbd3637835f7f2631d2ab Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Wed, 11 Mar 2009 18:53:24 +1100 Subject: [PATCH 29/31] Added Passive Analytics icon --- PASSIVE_ANALYTICS_README.txt | 17 ----------------- PassiveAnalytics_main.png | Bin 42541 -> 0 bytes PassiveAnalytics_settings.png | Bin 29298 -> 0 bytes sbin/installPassiveAnalytics.pl | 2 +- www/extras/adminConsole/passiveAnalytics.png | Bin 0 -> 4788 bytes .../adminConsole/small/passiveAnalytics.png | Bin 0 -> 966 bytes 6 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 PASSIVE_ANALYTICS_README.txt delete mode 100644 PassiveAnalytics_main.png delete mode 100644 PassiveAnalytics_settings.png create mode 100644 www/extras/adminConsole/passiveAnalytics.png create mode 100644 www/extras/adminConsole/small/passiveAnalytics.png diff --git a/PASSIVE_ANALYTICS_README.txt b/PASSIVE_ANALYTICS_README.txt deleted file mode 100644 index 638adc202..000000000 --- a/PASSIVE_ANALYTICS_README.txt +++ /dev/null @@ -1,17 +0,0 @@ -To try out Passive Analytics, run: - - sbin/installPassiveAnalytics.pl - -or even better: - - wgd reset --dev --util=sbin/installPassiveAnalytics.pl - -Quick Tour -* Go into the Passive Analytics section of the Admin Console -* Create some buckets -* Click around the site (and watch the passiveLog table get populated) -* Click "Begin Analysis" -* Wait for an email, or refresh to see the running status -* Download the Bucket Data, Delta Data and/or Raw Logs and view in a spreadsheet program - -For more info, see: http://www.plainblack.com/rfe/request-for-enhancement/8862 diff --git a/PassiveAnalytics_main.png b/PassiveAnalytics_main.png deleted file mode 100644 index 9ee8931796e442166ddcc862e4f490ed0fc09753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42541 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 Q5x{}ItfThgl6A=c0DV%#(EtDd diff --git a/PassiveAnalytics_settings.png b/PassiveAnalytics_settings.png deleted file mode 100644 index 0bdee2ef44c309432aa802d436d43d09368220eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29298 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%0AZw7ouQc(t{q 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 diff --git a/sbin/installPassiveAnalytics.pl b/sbin/installPassiveAnalytics.pl index cad304e45..acf0fd8bc 100644 --- a/sbin/installPassiveAnalytics.pl +++ b/sbin/installPassiveAnalytics.pl @@ -138,7 +138,7 @@ sub installPassiveAnalyticsConfig { my $adminConsole = $session->config->get('adminConsole'); if (!exists $adminConsole->{'passiveAnalytics'}) { $adminConsole->{'passiveAnalytics'} = { - "icon" => "statistics.gif", + "icon" => "passiveAnalytics.png", "uiLevel" => 1, "url" => "^PageUrl(\"\",op=passiveAnalytics;func=editRuleflow);", "title" => "^International(Passive Analytics,PassiveAnalytics);", diff --git a/www/extras/adminConsole/passiveAnalytics.png b/www/extras/adminConsole/passiveAnalytics.png new file mode 100644 index 0000000000000000000000000000000000000000..131ebdad8e33f7ddd55f1dc71967b768c0b5151e GIT binary patch literal 4788 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 O0000u?#I^B{;<&5^@r^!6vhVqpkKeLP-wA& z0lRfu=UVG5Wip-7#Mr2bCdS4a^L1n2L}~HBx#yn4d7ksU@B6%r=UNfg#|!AojvzjI z4dV9Z6N0^^nG>BA0K=&98DApU=T@Hed@t5~t1ge72ct3OQQSIs6;N{P^0|^P(non= zH00WE{xI#jus45wEe)h!(3;6@XtFeQ z;B*EQyDlH*3+(&Ck4`&Lz=G3gB@{ZEy7J)EH1P!yUJSu(v(dl0Sw}MW(Zf*FS(Z`! z-OB7hE91SwprpBXjn~pEnh@LrqZpi&(Yzys+7D{;>NbtFED16}*R;-_E~9RvsxK!uu2!~MjM>TOoo%MCnjZ2|xEG5%G*+(({ZI#VCQQl@&Cr%)>`h7>J!gLUe(X zwLp|X{jLi1|LG+H0|ZGz@AZ%Ij`4q->Q>RWbPU5IerO5HB@-*tR=L#T>Ai)zGKx35 zl}bp7tu(_P$Y9hRKs+g;r>h#U3rJsmj=|wyaj?OHY;taqi$w$d+gy#cKUjE}#B!>H zj*><(3OwwNEZWchh4!|$fbtq3tf2Lf8T*%3;i%k-rJ<20tP+~-$ofZMt~vnLK$m!` zzs^nsrSJw4XleTj)8RsLDp`Kb2Ay03$Ge{Z^G~r7@*QWv>k)bW_$C(akTc&S@S8+J zpmKG?*z1@PFDpKxJZc`Oa8%!;EHCM$s2T8U4hwA>NQjyE`#+u+EW-= zssKl!XbaY@oEB*%XGVTOZ2V$sVah|^)^9YEM#7@c>&&Enx@B}sv}$ZE^xAfkERz!I z2bKCF^#E${E|&fG9zY+8{q3a4ZTE=ME0%d;GBw3433nuG6PLORm8)lM`P$D}2 oSeXxZiX}UI(cs)GXTKC+0D>JzaiOMDzyJUM07*qoM6N<$f{H@bpa1{> literal 0 HcmV?d00001 From a36750fde39b3e406dff1269d1b0be69b2de48ef Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Wed, 11 Mar 2009 18:57:21 +1100 Subject: [PATCH 30/31] Removed DataTable edits from this topic branch --- lib/WebGUI/Asset/Wobject/DataTable.pm | 1 - lib/WebGUI/Form/DataTable.pm | 7 +++-- lib/WebGUI/HTMLForm.pm | 3 +-- lib/WebGUI/i18n/English/Form_DataTable.pm | 7 ----- www/extras/yui-webgui/build/form/datatable.js | 26 +++++-------------- 5 files changed, 10 insertions(+), 34 deletions(-) diff --git a/lib/WebGUI/Asset/Wobject/DataTable.pm b/lib/WebGUI/Asset/Wobject/DataTable.pm index da18f460a..e8f25fd93 100644 --- a/lib/WebGUI/Asset/Wobject/DataTable.pm +++ b/lib/WebGUI/Asset/Wobject/DataTable.pm @@ -125,7 +125,6 @@ sub getDataTemplateVars { my $self = shift; my $json = $self->getDataJson; - $self->session->log->warn($json); my $dt = JSON->new->decode($json); # Make row data more friendly to templates diff --git a/lib/WebGUI/Form/DataTable.pm b/lib/WebGUI/Form/DataTable.pm index a43a549c9..3ebe5678a 100644 --- a/lib/WebGUI/Form/DataTable.pm +++ b/lib/WebGUI/Form/DataTable.pm @@ -122,11 +122,11 @@ sub getDataTableHtml { # Not using a datastructure and JSON.pm because of function references for "parser" my $columnDef = '{' - . qq["key" : "$column->{ key }", ] - . qq["abbr" : "$column->{ key }", ] + . qq["key" : "$column->{ key }", ] + . qq["abbr" : "$column->{ key }", ] . qq["formatter" : "$column->{ formatter }", ] . qq["resizable" : 1, ] - . qq["sortable" : $column->{sortable} ]; + . qq["sortable" : 1]; # Automatically determine the parser to use if ( $parsers{ $column->{formatter} } ) { @@ -186,7 +186,6 @@ sub getDefaultValue { columns => [ { key => "New Column", formatter => "text", - sortable => "true", }, ], rows => [ { "New Column" => "Value", }, ], diff --git a/lib/WebGUI/HTMLForm.pm b/lib/WebGUI/HTMLForm.pm index 47decd4eb..683f21e83 100644 --- a/lib/WebGUI/HTMLForm.pm +++ b/lib/WebGUI/HTMLForm.pm @@ -155,7 +155,6 @@ a C method to provide that information. sub dynamicForm { my ($self, $formDefinition, $fieldList, $parent) = @_; - my $form = $self->session->form; foreach my $definition (reverse @{$formDefinition}) { my $properties = $definition->{$fieldList}; foreach my $fieldname (keys %{$properties}) { @@ -166,7 +165,7 @@ sub dynamicForm { $params{$key} = $formDefinition->[0]{name}; } } - $params{value} = $form->get($fieldname) || $parent->get($fieldname); + $params{value} = $parent->get($fieldname); $params{name} = $fieldname; $self->dynamicField(%params); } diff --git a/lib/WebGUI/i18n/English/Form_DataTable.pm b/lib/WebGUI/i18n/English/Form_DataTable.pm index 26e80a527..ad51bad68 100644 --- a/lib/WebGUI/i18n/English/Form_DataTable.pm +++ b/lib/WebGUI/i18n/English/Form_DataTable.pm @@ -139,13 +139,6 @@ our $I18N = { lastUpdated => 0, context => q{Button to delete a column, shown in the Edit Schema dialog}, }, - - "sortable" => { - message => q{sortable}, - lastUpdated => 0, - context => q{Whether a column is sortable in the table}, - }, - }; 1; diff --git a/www/extras/yui-webgui/build/form/datatable.js b/www/extras/yui-webgui/build/form/datatable.js index a96db2181..cedbf8ae9 100644 --- a/www/extras/yui-webgui/build/form/datatable.js +++ b/www/extras/yui-webgui/build/form/datatable.js @@ -338,8 +338,7 @@ WebGUI.Form.DataTable "help select row", "help add row", "help default sort", - "help reorder column", - "sortable" + "help reorder column" ] }, onpreload : { @@ -400,8 +399,7 @@ WebGUI.Form.DataTable div.parentNode.removeChild( div ); }; - var sortableLabel = this.i18n.get( "Form_DataTable", "sortable" ); - var buttonLabel = this.i18n.get( "Form_DataTable", "delete column" ); + var buttonLabel = this.i18n.get( "Form_DataTable", "delete column" ); var availableFormats = [ { "value" : "text", @@ -466,16 +464,6 @@ WebGUI.Form.DataTable format.appendChild( opt ); } div.appendChild( format ); - - div.appendChild( document.createTextNode(sortableLabel) ); - - var sorter = document.createElement( "input" ); - sorter.type = "checkbox"; - sorter.name = "canSort_" + i; - sorter.checked = cols[i].sortable; - sorter.value = 1; - div.appendChild( sorter ); - return div; }; @@ -487,7 +475,6 @@ WebGUI.Form.DataTable var newIdx = cols.length; // create a new column object cols[newIdx] = new YAHOO.widget.Column; - cols[newIdx].sortable = true; // add it to the dialog box this.appendChild( createTableColumn(newIdx,cols) ); }; @@ -613,10 +600,9 @@ WebGUI.Form.DataTable // Update columns var i = 0; while ( data[ "newKey_" + i ] ) { - var oldKey = data[ "oldKey_" + i ]; - var newKey = data[ "newKey_" + i ]; - var format = data[ "format_" + i ][0]; - var canSort = data[ "canSort_" + i ] + var oldKey = data[ "oldKey_" + i ]; + var newKey = data[ "newKey_" + i ]; + var format = data[ "format_" + i ][0]; var col = this.dataTable.getColumn( oldKey ); // Don't allow adding multiple columns with same key @@ -640,7 +626,7 @@ WebGUI.Form.DataTable key : newKey, formatter : format, resizeable : ( col ? col.resizeable : 1 ), - sortable : canSort + sortable : ( col ? col.sortable : 1 ) }; var newIndex = col ? col.getKeyIndex() : undefined; From 9dfef4942c4f2aa5c97b5e52578e26700567e730 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Wed, 11 Mar 2009 19:03:37 +1100 Subject: [PATCH 31/31] Added Passive Analytics to upgrade script --- docs/upgrades/upgrade_7.6.14-7.7.0.pl | 167 +++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/docs/upgrades/upgrade_7.6.14-7.7.0.pl b/docs/upgrades/upgrade_7.6.14-7.7.0.pl index c915b2fe1..c3902cae3 100644 --- a/docs/upgrades/upgrade_7.6.14-7.7.0.pl +++ b/docs/upgrades/upgrade_7.6.14-7.7.0.pl @@ -22,7 +22,8 @@ use Getopt::Long; use WebGUI::Session; use WebGUI::Storage; use WebGUI::Asset; - +use WebGUI::PassiveAnalytics::Rule; +use WebGUI::Utility; my $toVersion = '7.7.0'; my $quiet; # this line required @@ -37,6 +38,14 @@ addScreenshotTemplatesToMatrix( $session ); surveyDoAfterTimeLimit($session); surveyRemoveResponseTemplate($session); +# Passive Analytics +pa_installLoggingTables($session); +pa_installPassiveAnalyticsRule($session); +pa_installPassiveAnalyticsConfig($session); +pa_installWorkflow($session); +pa_addPassiveAnalyticsSettings($session); +pa_addPassiveAnalyticsStatus($session); + finish($session); # this line required #---------------------------------------------------------------------------- @@ -82,6 +91,162 @@ sub surveyRemoveResponseTemplate { print "DONE!\n" unless $quiet; } +sub pa_installLoggingTables { + my $session = shift; + print "\tInstall logging tables... "; + my $db = $session->db; + $db->write(<write(<write(<write(<write(<write(<crud_createTable($session); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the PassiveAnalytics Settings +sub pa_addPassiveAnalyticsSettings { + my $session = shift; + print "\tInstall Passive Analytics settings... "; + # and here's our code + $session->setting->add('passiveAnalyticsInterval', 300); + $session->setting->add('passiveAnalyticsDeleteDelta', 0); + $session->setting->add('passiveAnalyticsEnabled', 0); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the PassiveAnalytics Rule table +sub pa_addPassiveAnalyticsStatus { + my $session = shift; + my $db = $session->db; + print "\tInstall Passive Analytics status table... "; + # and here's our code + $db->write(<write(<write('insert into passiveAnalyticsStatus (userId) VALUES (3)'); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the Passive Analytics config file entry +# for the adminConsole and the content handler +sub pa_installPassiveAnalyticsConfig { + my $session = shift; + print "\tAdd Passive Analytics entry to the config file... "; + # Admin Bar/Console + my $adminConsole = $session->config->get('adminConsole'); + if (!exists $adminConsole->{'passiveAnalytics'}) { + $adminConsole->{'passiveAnalytics'} = { + "icon" => "passiveAnalytics.png", + "uiLevel" => 1, + "url" => "^PageUrl(\"\",op=passiveAnalytics;func=editRuleflow);", + "title" => "^International(Passive Analytics,PassiveAnalytics);", + "groupSetting" => "3", + }; + $session->config->set('adminConsole', $adminConsole); + } + # Content Handler + my $contentHandlers = $session->config->get('contentHandlers'); + if (!isIn('WebGUI::Content::PassiveAnalytics',@{ $contentHandlers} ) ) { + my $contentIndex = 0; + HANDLER: while ($contentIndex <= $#{ $contentHandlers } ) { + print $contentHandlers->[$contentIndex]."\n"; + ##Insert before Operation + if($contentHandlers->[$contentIndex] eq 'WebGUI::Content::Operation') { + splice @{ $contentHandlers }, $contentIndex, 0, 'WebGUI::Content::PassiveAnalytics'; + last HANDLER; + } + ++$contentIndex; + } + $session->config->set('contentHandlers', $contentHandlers); + } + # Workflow Activities + my $workflowActivities = $session->config->get('workflowActivities'); + my @none = @{ $workflowActivities->{'None'} }; + if (!isIn('WebGUI::Workflow::Activity::SummarizePassiveAnalytics', @none)) { + push @none, 'WebGUI::Workflow::Activity::SummarizePassiveAnalytics'; + } + if (!isIn('WebGUI::Workflow::Activity::BucketPassiveAnalytics', @none)) { + push @none, 'WebGUI::Workflow::Activity::BucketPassiveAnalytics'; + } + $workflowActivities->{'None'} = [ @none ]; + $session->config->set('workflowActivities', $workflowActivities); + print "DONE!\n"; +} + +#---------------------------------------------------------------------------- +# Add the Passive Analytics Workflow +sub pa_installWorkflow { + my $session = shift; + print "\tAdd Passive Analytics Workflow... "; + my $workflow = WebGUI::Workflow->create( + $session, + { + title => 'Analyze Passive Analytics', + mode => 'singleton', + type => 'None', + description => 'Manual changes to this workflow will be lost. Please only use the Passive Analytics screen to make changes', + }, + 'PassiveAnalytics000001', + ); + my $summarize = $workflow->addActivity('WebGUI::Workflow::Activity::SummarizePassiveAnalytics'); + my $bucket = $workflow->addActivity('WebGUI::Workflow::Activity::BucketPassiveAnalytics'); + $summarize->set('title', 'Perform duration analysis'); + $bucket->set( 'title', 'Please log entries into buckets'); + $workflow->set({enabled => 1}); + print "DONE!\n"; +} + #---------------------------------------------------------------------------- # Describe what our function does