aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Script
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/Script')
-rw-r--r--perllib/FixMyStreet/Script/Alerts.pm15
-rw-r--r--perllib/FixMyStreet/Script/Questionnaires.pm3
-rw-r--r--perllib/FixMyStreet/Script/Reports.pm2
-rwxr-xr-xperllib/FixMyStreet/Script/UpdateAllReports.pm274
4 files changed, 284 insertions, 10 deletions
diff --git a/perllib/FixMyStreet/Script/Alerts.pm b/perllib/FixMyStreet/Script/Alerts.pm
index 1a760a0c1..c001cc311 100644
--- a/perllib/FixMyStreet/Script/Alerts.pm
+++ b/perllib/FixMyStreet/Script/Alerts.pm
@@ -6,7 +6,6 @@ use warnings;
use DateTime::Format::Pg;
use IO::String;
-use mySociety::DBHandle qw(dbh);
use FixMyStreet::Gaze;
use mySociety::Locale;
use mySociety::MaPit;
@@ -18,8 +17,6 @@ use FixMyStreet::Email;
use FixMyStreet::Map;
use FixMyStreet::App::Model::PhotoSet;
-FixMyStreet->configure_mysociety_dbhandle;
-
my $parser = DateTime::Format::Pg->new();
# Child must have confirmed, id, email, state(!) columns
@@ -65,7 +62,7 @@ sub send() {
$query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
$query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
- $query = dbh()->prepare($query);
+ $query = FixMyStreet::DB->schema->storage->dbh->prepare($query);
$query->execute();
my $last_alert_id;
my %data = ( template => $alert_type->template, data => [], schema => $schema );
@@ -105,7 +102,7 @@ sub send() {
my $url = $cobrand->base_url_for_report($row);
# this is currently only for new_updates
- if ($row->{item_text}) {
+ if (defined($row->{item_text})) {
if ( $cobrand->moniker ne 'zurich' && $row->{alert_user_id} == $row->{user_id} ) {
# This is an alert to the same user who made the report - make this a login link
# Don't bother with Zurich which has no accounts
@@ -143,7 +140,7 @@ sub send() {
# this is ward and council problems
} else {
if ( exists $row->{geocode} && $row->{geocode} && $ref =~ /ward|council/ ) {
- my $nearest_st = _get_address_from_gecode( $row->{geocode} );
+ my $nearest_st = _get_address_from_geocode( $row->{geocode} );
$row->{nearest} = $nearest_st;
}
@@ -228,7 +225,7 @@ sub send() {
and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
and users.email <> ?
order by confirmed desc";
- $q = dbh()->prepare($q);
+ $q = FixMyStreet::DB->schema->storage->dbh->prepare($q);
$q->execute($latitude, $longitude, $d, $alert->whensubscribed, $alert->id, $alert->user->email);
while (my $row = $q->fetchrow_hashref) {
$schema->resultset('AlertSent')->create( {
@@ -236,7 +233,7 @@ sub send() {
parameter => $row->{id},
} );
if ( exists $row->{geocode} && $row->{geocode} ) {
- my $nearest_st = _get_address_from_gecode( $row->{geocode} );
+ my $nearest_st = _get_address_from_geocode( $row->{geocode} );
$row->{nearest} = $nearest_st;
}
my $dt = $parser->parse_timestamp( $row->{confirmed} );
@@ -304,7 +301,7 @@ sub _send_aggregated_alert_email(%) {
}
}
-sub _get_address_from_gecode {
+sub _get_address_from_geocode {
my $geocode = shift;
return '' unless defined $geocode;
diff --git a/perllib/FixMyStreet/Script/Questionnaires.pm b/perllib/FixMyStreet/Script/Questionnaires.pm
index 3f22eb150..ec6139d2d 100644
--- a/perllib/FixMyStreet/Script/Questionnaires.pm
+++ b/perllib/FixMyStreet/Script/Questionnaires.pm
@@ -16,6 +16,9 @@ sub send {
sub send_questionnaires_period {
my ( $period, $params ) = @_;
+ # Don't send if we don't have a fixed state
+ return unless FixMyStreet::DB::Result::Problem::fixed_states->{fixed};
+
my $rs = FixMyStreet::DB->resultset('Questionnaire');
# Select all problems that need a questionnaire email sending
diff --git a/perllib/FixMyStreet/Script/Reports.pm b/perllib/FixMyStreet/Script/Reports.pm
index 6057807de..1e5fd55bb 100644
--- a/perllib/FixMyStreet/Script/Reports.pm
+++ b/perllib/FixMyStreet/Script/Reports.pm
@@ -98,7 +98,7 @@ sub send(;$) {
$h{osm_url} = Utils::OpenStreetMap::short_url($h{latitude}, $h{longitude});
if ( $row->used_map ) {
- $h{closest_address} = $cobrand->find_closest( $h{latitude}, $h{longitude}, $row );
+ $h{closest_address} = $cobrand->find_closest($row);
$h{osm_url} .= '?m';
}
diff --git a/perllib/FixMyStreet/Script/UpdateAllReports.pm b/perllib/FixMyStreet/Script/UpdateAllReports.pm
new file mode 100755
index 000000000..1bd069ee8
--- /dev/null
+++ b/perllib/FixMyStreet/Script/UpdateAllReports.pm
@@ -0,0 +1,274 @@
+package FixMyStreet::Script::UpdateAllReports;
+
+use strict;
+use warnings;
+
+use FixMyStreet;
+use FixMyStreet::DB;
+
+use File::Path ();
+use File::Slurp;
+use JSON::MaybeXS;
+use List::MoreUtils qw(zip);
+use List::Util qw(sum);
+
+my $fourweeks = 4*7*24*60*60;
+
+# Age problems from when they're confirmed, except on Zurich
+# where they appear as soon as they're created.
+my $age_column = 'confirmed';
+if ( FixMyStreet->config('BASE_URL') =~ /zurich|zueri/ ) {
+ $age_column = 'created';
+}
+
+sub generate {
+ my $include_areas = shift;
+
+ my $problems = FixMyStreet::DB->resultset('Problem')->search(
+ {
+ state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
+ bodies_str => \'is not null',
+ },
+ {
+ columns => [
+ 'id', 'bodies_str', 'state', 'areas', 'cobrand',
+ { duration => { extract => "epoch from current_timestamp-lastupdate" } },
+ { age => { extract => "epoch from current_timestamp-$age_column" } },
+ ]
+ }
+ );
+ $problems = $problems->cursor; # Raw DB cursor for speed
+
+ my ( %fixed, %open );
+ my @cols = ( 'id', 'bodies_str', 'state', 'areas', 'cobrand', 'duration', 'age' );
+ while ( my @problem = $problems->next ) {
+ my %problem = zip @cols, @problem;
+ my @bodies;
+ my @areas;
+ my $cobrand = $problem{cobrand};
+ my $duration_str = ( $problem{duration} > 2 * $fourweeks ) ? 'old' : 'new';
+ my $type = ( $problem{duration} > 2 * $fourweeks )
+ ? 'unknown'
+ : ($problem{age} > $fourweeks ? 'older' : 'new');
+ my $problem_fixed =
+ FixMyStreet::DB::Result::Problem->fixed_states()->{$problem{state}}
+ || FixMyStreet::DB::Result::Problem->closed_states()->{$problem{state}};
+
+ # Add to bodies it was sent to
+ @bodies = split( /,/, $problem{bodies_str} );
+
+ foreach my $body ( @bodies ) {
+ if ( $problem_fixed ) {
+ # Fixed problems are either old or new
+ $fixed{$body}{$duration_str}++;
+ $fixed{$cobrand}{$body}{$duration_str}++;
+ } else {
+ # Open problems are either unknown, older, or new
+ $open{$body}{$type}++;
+ $open{$cobrand}{$body}{$type}++;
+ }
+ }
+
+ if ( $include_areas ) {
+ @areas = grep { $_ } split( /,/, $problem{areas} );
+ foreach my $area ( @areas ) {
+ if ( $problem_fixed ) {
+ $fixed{areas}{$area}{$duration_str}++;
+ } else {
+ $open{areas}{$area}{$type}++;
+ }
+ }
+ }
+ }
+
+ my $body = encode_json( {
+ fixed => \%fixed,
+ open => \%open,
+ } );
+
+ File::Path::mkpath( FixMyStreet->path_to( '../data/' )->stringify );
+ File::Slurp::write_file( FixMyStreet->path_to( '../data/all-reports.json' )->stringify, \$body );
+}
+
+sub end_period {
+ my $period = shift;
+ FixMyStreet->set_time_zone(DateTime->now)->truncate(to => $period)->add($period . 's' => 1)->subtract(seconds => 1);
+}
+
+sub loop_period {
+ my ($date, $period, $extra) = @_;
+ my $end = end_period($period);
+ my @out;
+ while ($date <= $end) {
+ push @out, { n => $date->$period, $extra ? (d => $date->$extra) : () };
+ $date->add($period . 's' => 1);
+ }
+ return @out;
+}
+
+sub generate_dashboard {
+ my %data;
+
+ my $end_today = end_period('day');
+ my $min_confirmed = FixMyStreet::DB->resultset('Problem')->search({
+ state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
+ }, {
+ select => [ { min => 'confirmed' } ],
+ as => [ 'confirmed' ],
+ })->first->confirmed;
+ if ($min_confirmed) {
+ $min_confirmed = $min_confirmed->truncate(to => 'day');
+ } else {
+ $min_confirmed = FixMyStreet->set_time_zone(DateTime->now)->truncate(to => 'day');
+ }
+
+ my ($group_by, $extra);
+ if (DateTime::Duration->compare($end_today - $min_confirmed, DateTime::Duration->new(months => 1)) < 0) {
+ $group_by = 'day';
+ } elsif (DateTime::Duration->compare($end_today - $min_confirmed, DateTime::Duration->new(years => 1)) < 0) {
+ $group_by = 'month';
+ $extra = 'month_abbr';
+ } else {
+ $group_by = 'year';
+ }
+ my @problem_periods = loop_period($min_confirmed, $group_by, $extra);
+
+ my %problems_reported_by_period = stuff_by_day_or_year(
+ $group_by, 'Problem',
+ state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
+ );
+ my %problems_fixed_by_period = stuff_by_day_or_year(
+ $group_by, 'Problem',
+ state => [ FixMyStreet::DB::Result::Problem->fixed_states() ],
+ );
+
+ my (@problems_reported_by_period, @problems_fixed_by_period);
+ foreach (map { $_->{n} } @problem_periods) {
+ push @problems_reported_by_period, ($problems_reported_by_period[-1]||0) + ($problems_reported_by_period{$_}||0);
+ push @problems_fixed_by_period, ($problems_fixed_by_period[-1]||0) + ($problems_fixed_by_period{$_}||0);
+ }
+ $data{problem_periods} = [ map { $_->{d} || $_->{n} } @problem_periods ];
+ $data{problems_reported_by_period} = \@problems_reported_by_period;
+ $data{problems_fixed_by_period} = \@problems_fixed_by_period;
+
+ my %last_seven_days = (
+ problems => [],
+ updated => [],
+ fixed => [],
+ );
+ $data{last_seven_days} = \%last_seven_days;
+
+ my $dtf = FixMyStreet::DB->schema->storage->datetime_parser;
+ my $eight_ago = $dtf->format_datetime(DateTime->now->subtract(days => 8));
+ %problems_reported_by_period = stuff_by_day_or_year('day',
+ 'Problem',
+ state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
+ confirmed => { '>=', $eight_ago },
+ );
+ %problems_fixed_by_period = stuff_by_day_or_year('day',
+ 'Comment',
+ confirmed => { '>=', $eight_ago },
+ -or => [
+ problem_state => [ FixMyStreet::DB::Result::Problem->fixed_states() ],
+ mark_fixed => 1,
+ ],
+ );
+ my %problems_updated_by_period = stuff_by_day_or_year('day',
+ 'Comment',
+ confirmed => { '>=', $eight_ago },
+ );
+
+ my $date = DateTime->today->subtract(days => 7);
+ while ($date < DateTime->today) {
+ push @{$last_seven_days{problems}}, $problems_reported_by_period{$date->day} || 0;
+ push @{$last_seven_days{fixed}}, $problems_fixed_by_period{$date->day} || 0;
+ push @{$last_seven_days{updated}}, $problems_updated_by_period{$date->day} || 0;
+ $date->add(days => 1);
+ }
+ $last_seven_days{problems_total} = sum @{$last_seven_days{problems}};
+ $last_seven_days{fixed_total} = sum @{$last_seven_days{fixed}};
+ $last_seven_days{updated_total} = sum @{$last_seven_days{updated}};
+
+ my(@top_five_bodies);
+ $data{top_five_bodies} = \@top_five_bodies;
+
+ my $bodies = FixMyStreet::DB->resultset('Body')->search;
+ my $substmt = "select min(id) from comment where me.problem_id=comment.problem_id and (problem_state in ('fixed', 'fixed - council', 'fixed - user') or mark_fixed)";
+ while (my $body = $bodies->next) {
+ my $subquery = FixMyStreet::DB->resultset('Comment')->to_body($body)->search({
+ -or => [
+ problem_state => [ FixMyStreet::DB::Result::Problem->fixed_states() ],
+ mark_fixed => 1,
+ ],
+ 'me.id' => \"= ($substmt)",
+ 'me.state' => 'confirmed',
+ }, {
+ select => [
+ { extract => "epoch from me.confirmed-problem.confirmed", -as => 'time' },
+ ],
+ as => [ qw/time/ ],
+ rows => 100,
+ order_by => { -desc => 'me.confirmed' },
+ join => 'problem'
+ })->as_subselect_rs;
+ my $avg = $subquery->search({
+ }, {
+ select => [ { avg => "time" } ],
+ as => [ qw/avg/ ],
+ })->first->get_column('avg');
+ push @top_five_bodies, { name => $body->name, days => int($avg / 60 / 60 / 24 + 0.5) }
+ if defined $avg;
+ }
+ @top_five_bodies = sort { $a->{days} <=> $b->{days} } @top_five_bodies;
+ $data{average} = @top_five_bodies
+ ? int((sum map { $_->{days} } @top_five_bodies) / @top_five_bodies + 0.5) : undef;
+
+ @top_five_bodies = @top_five_bodies[0..4] if @top_five_bodies > 5;
+
+ my $week_ago = $dtf->format_datetime(DateTime->now->subtract(days => 7));
+ my $last_seven_days = FixMyStreet::DB->resultset("Problem")->search({
+ confirmed => { '>=', $week_ago },
+ })->count;
+ my @top_five_categories = FixMyStreet::DB->resultset("Problem")->search({
+ confirmed => { '>=', $week_ago },
+ category => { '!=', 'Other' },
+ }, {
+ select => [ 'category', { count => 'id' } ],
+ as => [ 'category', 'count' ],
+ group_by => 'category',
+ rows => 5,
+ order_by => { -desc => 'count' },
+ });
+ $data{top_five_categories} = [ map {
+ { category => $_->category, count => $_->get_column('count') }
+ } @top_five_categories ];
+ foreach (@top_five_categories) {
+ $last_seven_days -= $_->get_column('count');
+ }
+ $data{other_categories} = $last_seven_days;
+
+ my $body = encode_json( \%data );
+ File::Path::mkpath( FixMyStreet->path_to( '../data/' )->stringify );
+ File::Slurp::write_file( FixMyStreet->path_to( '../data/all-reports-dashboard.json' )->stringify, \$body );
+}
+
+sub stuff_by_day_or_year {
+ my $period = shift;
+ my $table = shift;
+ my %params = @_;
+ my $results = FixMyStreet::DB->resultset($table)->search({
+ %params
+ }, {
+ select => [ { extract => \"$period from confirmed", -as => $period }, { count => 'id' } ],
+ as => [ $period, 'count' ],
+ group_by => [ $period ],
+ });
+ my %out;
+ while (my $row = $results->next) {
+ my $p = $row->get_column($period);
+ $out{$p} = $row->get_column('count');
+ }
+ return %out;
+}
+
+1;