From 0b1ab9fb1145ca32dced46b4faefe49da5c76768 Mon Sep 17 00:00:00 2001 From: pezholio Date: Fri, 20 Jan 2017 14:46:01 +0000 Subject: Utils::prettify_duration improvements - Add year and month to $nearest options - Allow prettify_duration to choose a sensible $nearest if omitted --- perllib/Utils.pm | 33 ++++++++++++++++++++++++++++++--- t/utils.t | 8 ++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/perllib/Utils.pm b/perllib/Utils.pm index 7dd2a3f39..6ba20e9d3 100644 --- a/perllib/Utils.pm +++ b/perllib/Utils.pm @@ -99,7 +99,7 @@ sub truncate_coordinate { Strip leading and trailing white space from a string. Also reduces all white space to a single space. -Trim +Trim =cut @@ -195,7 +195,28 @@ sub prettify_dt { # argument is duration in seconds, rounds to the nearest minute sub prettify_duration { my ($s, $nearest) = @_; - if ($nearest eq 'week') { + + unless ( defined $nearest ) { + if ($s < 3600) { + $nearest = 'minute'; + } elsif ($s < 3600*24) { + $nearest = 'hour'; + } elsif ($s < 3600*24*7) { + $nearest = 'day'; + } elsif ($s < 3600*24*7*4) { + $nearest = 'week'; + } elsif ($s < 3600*24*7*4*12) { + $nearest = 'month'; + } else { + $nearest = 'year'; + } + } + + if ($nearest eq 'year') { + $s = int(($s+60*60*24*3.5)/60/60/24/7/4/12)*60*60*24*7*4*12; + } elsif ($nearest eq 'month') { + $s = int(($s+60*60*24*3.5)/60/60/24/7/4)*60*60*24*7*4; + } elsif ($nearest eq 'week') { $s = int(($s+60*60*24*3.5)/60/60/24/7)*60*60*24*7; } elsif ($nearest eq 'day') { $s = int(($s+60*60*12)/60/60/24)*60*60*24; @@ -206,6 +227,8 @@ sub prettify_duration { return _('less than a minute') if $s == 0; } my @out = (); + _part(\$s, 60*60*24*7*4*12, \@out); + _part(\$s, 60*60*24*7*4, \@out); _part(\$s, 60*60*24*7, \@out); _part(\$s, 60*60*24, \@out); _part(\$s, 60*60, \@out); @@ -217,7 +240,11 @@ sub _part { if ($$s >= $m) { my $i = int($$s / $m); my $str; - if ($m == 60*60*24*7) { + if ($m == 60*60*24*7*4*12) { + $str = mySociety::Locale::nget("%d year", "%d years", $i); + } elsif ($m == 60*60*24*7*4) { + $str = mySociety::Locale::nget("%d month", "%d months", $i); + } elsif ($m == 60*60*24*7) { $str = mySociety::Locale::nget("%d week", "%d weeks", $i); } elsif ($m == 60*60*24) { $str = mySociety::Locale::nget("%d day", "%d days", $i); diff --git a/t/utils.t b/t/utils.t index d6c56d95a..f989580c8 100644 --- a/t/utils.t +++ b/t/utils.t @@ -91,10 +91,18 @@ is Utils::prettify_dt($dt, 1), $dt->strftime("%H:%M, %e %b %Y"); $dt = DateTime->now->subtract(days => 400); is Utils::prettify_dt($dt), $dt->strftime("%H:%M, %a %e %B %Y"); +is Utils::prettify_duration(12*5*7*86400+3600+60+1, 'year'), '1 year'; +is Utils::prettify_duration(25*5*7*86400+3600+60+1, 'year'), '2 years'; +is Utils::prettify_duration(5*7*86400+3600+60+1, 'month'), '1 month'; is Utils::prettify_duration(7*86400+3600+60+1, 'week'), '1 week'; is Utils::prettify_duration(86400+3600+60+1, 'day'), '1 day'; is Utils::prettify_duration(86400+3600+60+1, 'hour'), '1 day, 1 hour'; is Utils::prettify_duration(86400+3600+60+1, 'minute'), '1 day, 1 hour, 1 minute'; is Utils::prettify_duration(20, 'minute'), 'less than a minute'; +# prettify_duration should choose a $nearest sensibly if it's not given +is Utils::prettify_duration(12*5*7*86400+3600+60+1), '1 year'; +is Utils::prettify_duration(7*86400+3600+60+1), '1 week'; +is Utils::prettify_duration(14*86400+3600+60+1), '2 weeks'; +is Utils::prettify_duration(1800), '30 minutes'; done_testing(); -- cgit v1.2.3 From b14319e75b61ce1ee21ebb7d6fa924ebe18ceee9 Mon Sep 17 00:00:00 2001 From: pezholio Date: Tue, 17 Jan 2017 16:44:50 +0000 Subject: Add Problem->time_ago for pretty-printed duration --- perllib/FixMyStreet/DB/Result/Problem.pm | 14 ++++++++++ t/app/model/problem.t | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 0092dd8b5..d0a5a3a4f 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -659,6 +659,20 @@ sub body { return $body; } + +=head2 time_ago + Returns how long ago a problem was reported in an appropriately + prettified duration, depending on the duration. +=cut + +sub time_ago { + my ( $self, $date ) = @_; + $date ||= 'confirmed'; + my $duration = time() - $self->$date->epoch; + + return Utils::prettify_duration( $duration ); +} + =head2 response_templates Returns all ResponseTemplates attached to this problem's bodies, in alphabetical diff --git a/t/app/model/problem.t b/t/app/model/problem.t index 1130078c0..e2c407ffb 100644 --- a/t/app/model/problem.t +++ b/t/app/model/problem.t @@ -774,6 +774,50 @@ subtest 'check duplicate reports' => sub { is $problem2->duplicates->[0]->title, $problem1->title, 'problem2 includes problem1 in duplicates'; }; +subtest 'get report time ago in appropriate format' => sub { + my ($problem) = $mech->create_problems_for_body(1, $body_ids{2651}, 'TITLE'); + + $problem->update( { + confirmed => DateTime->now->subtract( minutes => 2) + } ); + is $problem->time_ago, '2 minutes', 'problem returns time ago in minutes'; + + $problem->update( { + confirmed => DateTime->now->subtract( hours => 18) + } ); + is $problem->time_ago, '18 hours', 'problem returns time ago in hours'; + + $problem->update( { + confirmed => DateTime->now->subtract( days => 4) + } ); + is $problem->time_ago, '4 days', 'problem returns time ago in days'; + + $problem->update( { + confirmed => DateTime->now->subtract( weeks => 3 ) + } ); + is $problem->time_ago, '3 weeks', 'problem returns time ago in weeks'; + + $problem->update( { + confirmed => DateTime->now->subtract( months => 4 ) + } ); + is $problem->time_ago, '4 months', 'problem returns time ago in months'; + + $problem->update( { + confirmed => DateTime->now->subtract( years => 2 ) + } ); + is $problem->time_ago, '2 years', 'problem returns time ago in years'; + +}; + +subtest 'time ago works with other dates' => sub { + my ($problem) = $mech->create_problems_for_body(1, $body_ids{2651}, 'TITLE'); + + $problem->update( { + lastupdate => DateTime->now->subtract( days => 4) + } ); + is $problem->time_ago('lastupdate'), '4 days', 'problem returns last updated time ago in days'; +}; + END { $problem->comments->delete if $problem; $problem->delete if $problem; -- cgit v1.2.3 From 2a099bc9b33ed0b40234a7070437f4defb44ce4b Mon Sep 17 00:00:00 2001 From: pezholio Date: Wed, 18 Jan 2017 11:20:37 +0000 Subject: Allow users to reopen closed reports Users can reopen their own reports when leaving an update. --- templates/web/base/report/update/form_update.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/templates/web/base/report/update/form_update.html b/templates/web/base/report/update/form_update.html index e0464eec3..f15a1f74b 100644 --- a/templates/web/base/report/update/form_update.html +++ b/templates/web/base/report/update/form_update.html @@ -47,10 +47,14 @@ [% END %] [% ELSE %] - [% IF problem.is_fixed AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %] + [% IF (problem.is_fixed OR problem.state == 'closed') AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %] - - + + [% IF problem.is_closed %] + + [% ELSE %] + + [% END %] [% ELSIF !problem.is_fixed %] -- cgit v1.2.3 From 3010d13823de9833700b18bf4ac3e71437d9001f Mon Sep 17 00:00:00 2001 From: pezholio Date: Wed, 18 Jan 2017 14:44:04 +0000 Subject: Add Problem->tokenised_url for logging user in This method creates a token that logs the reporting user in, optionally with some parameters which are stored with the token for use when redirecting after login. --- perllib/FixMyStreet/DB/Result/Problem.pm | 26 ++++++++++++++++++++++++++ t/app/model/problem.t | 31 +++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index d0a5a3a4f..97cb28fe8 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -182,6 +182,8 @@ use Utils; use FixMyStreet::Map::FMS; use LWP::Simple qw($ua); use RABX; +use URI; +use URI::QueryParam; my $IM = eval { require Image::Magick; @@ -511,6 +513,30 @@ sub admin_url { return $cobrand->admin_base_url . '/report_edit/' . $self->id; } +=head2 tokenised_url + +Return a url for this problem report that logs a user in + +=cut + +sub tokenised_url { + my ($self, $user, $params) = @_; + + my $token = FixMyStreet::App->model('DB::Token')->create( + { + scope => 'email_sign_in', + data => { + id => $self->id, + email => $user->email, + r => $self->url, + p => $params, + } + } + ); + + return "/M/". $token->token; +} + =head2 is_open Returns 1 if the problem is in a open state otherwise 0. diff --git a/t/app/model/problem.t b/t/app/model/problem.t index e2c407ffb..52213ed51 100644 --- a/t/app/model/problem.t +++ b/t/app/model/problem.t @@ -5,6 +5,7 @@ use Test::More; use FixMyStreet::TestMech; use FixMyStreet; +use FixMyStreet::App; use FixMyStreet::DB; use mySociety::Locale; use Sub::Override; @@ -53,7 +54,7 @@ is $problem->whensent, undef, 'inflating null confirmed ok'; is $problem->lastupdate, undef, 'inflating null confirmed ok'; is $problem->created, undef, 'inflating null confirmed ok'; -for my $test ( +for my $test ( { desc => 'more or less empty problem', changed => {}, @@ -242,7 +243,7 @@ for my $test ( }; } -for my $test ( +for my $test ( { state => 'partial', is_visible => 0, @@ -774,6 +775,32 @@ subtest 'check duplicate reports' => sub { is $problem2->duplicates->[0]->title, $problem1->title, 'problem2 includes problem1 in duplicates'; }; +subtest 'generates a tokenised url for a user' => sub { + my ($problem) = $mech->create_problems_for_body(1, $body_ids{2651}, 'TITLE'); + my $url = $problem->tokenised_url($user); + (my $token = $url) =~ s/\/M\///g; + + like $url, qr/\/M\//, 'problem generates tokenised url'; + + my $token_obj = FixMyStreet::App->model('DB::Token')->find( { + scope => 'email_sign_in', token => $token + } ); + is $token, $token_obj->token, 'token is generated in database with correct scope'; + is $token_obj->data->{r}, $problem->url, 'token has correct redirect data'; +}; + +subtest 'stores params in a token' => sub { + my ($problem) = $mech->create_problems_for_body(1, $body_ids{2651}, 'TITLE'); + my $url = $problem->tokenised_url($user, { foo => 'bar', baz => 'boo'}); + (my $token = $url) =~ s/\/M\///g; + + my $token_obj = FixMyStreet::App->model('DB::Token')->find( { + scope => 'email_sign_in', token => $token + } ); + + is_deeply $token_obj->data->{p}, { foo => 'bar', baz => 'boo'}, 'token has correct params'; +}; + subtest 'get report time ago in appropriate format' => sub { my ($problem) = $mech->create_problems_for_body(1, $body_ids{2651}, 'TITLE'); -- cgit v1.2.3 From 9efe4d14d1415e2fa060891b8be8ffec8b237911 Mon Sep 17 00:00:00 2001 From: pezholio Date: Wed, 18 Jan 2017 16:24:45 +0000 Subject: Use Token params for redirect on login Tokens can include a 'p' field in their data to set query params for the post-login redirect URL. --- perllib/FixMyStreet/App/Controller/Auth.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index b41e88209..70821f79d 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -223,7 +223,7 @@ sub token : Path('/M') : Args(1) { $c->authenticate( { email => $user->email }, 'no_password' ); # send the user to their page - $c->detach( 'redirect_on_signin', [ $data->{r} ] ); + $c->detach( 'redirect_on_signin', [ $data->{r}, $data->{p} ] ); } =head2 facebook_sign_in @@ -411,7 +411,7 @@ Used after signing in to take the person back to where they were. sub redirect_on_signin : Private { - my ( $self, $c, $redirect ) = @_; + my ( $self, $c, $redirect, $params ) = @_; unless ( $redirect ) { $c->detach('redirect_to_categories') if $c->user->from_body && scalar @{ $c->user->categories }; $redirect = 'my'; @@ -420,7 +420,11 @@ sub redirect_on_signin : Private { if ( $c->cobrand->moniker eq 'zurich' ) { $redirect = 'admin' if $c->user->from_body; } - $c->res->redirect( $c->uri_for( "/$redirect" ) ); + if (defined $params) { + $c->res->redirect( $c->uri_for( "/$redirect", $params ) ); + } else { + $c->res->redirect( $c->uri_for( "/$redirect" ) ); + } } =head2 redirect_to_categories -- cgit v1.2.3 From 8e6f6a1818b4f998d48f157387d2314bb8c86f8a Mon Sep 17 00:00:00 2001 From: pezholio Date: Wed, 18 Jan 2017 17:05:10 +0000 Subject: [Oxfordshire] Old report archiving script This script can be used for the bulk closure of old reports that haven't been updated in a long time. Reports that haven't been updated in a *really* long time (since 2014, by default) are simply closed, and those that haven't been updated since 2015 (by default) are closed and an email explaining the closure is sent to the report creator. This script can be used for other cobrands, but they'll need an archive.{txt,html} template in order for the emails to be sent. For mysociety/fixmystreetforcouncils#51 --- bin/oxfordshire/archive-old-enquiries | 29 ++++ perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm | 141 +++++++++++++++++++ t/app/script/archive_old_enquiries.t | 163 ++++++++++++++++++++++ templates/email/oxfordshire/archive.html | 58 ++++++++ templates/email/oxfordshire/archive.txt | 28 ++++ 5 files changed, 419 insertions(+) create mode 100755 bin/oxfordshire/archive-old-enquiries create mode 100644 perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm create mode 100644 t/app/script/archive_old_enquiries.t create mode 100644 templates/email/oxfordshire/archive.html create mode 100644 templates/email/oxfordshire/archive.txt diff --git a/bin/oxfordshire/archive-old-enquiries b/bin/oxfordshire/archive-old-enquiries new file mode 100755 index 000000000..7fe66703a --- /dev/null +++ b/bin/oxfordshire/archive-old-enquiries @@ -0,0 +1,29 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +require 5.8.0; + +BEGIN { + use File::Basename qw(dirname); + use File::Spec; + my $d = dirname(File::Spec->rel2abs($0)); + require "$d/../../setenv.pl"; +} + +use FixMyStreet::Script::ArchiveOldEnquiries; +use Getopt::Long::Descriptive; + +my ($opts, $usage) = describe_options( + '%c %o', + ['commit|c', "actually close reports and send emails. Omitting this flag will do a dry-run"], + ['body|b=s', "which body ID to close reports for"], + ['cobrand=s', "which cobrand template to use for sent emails"], + ['closure-cutoff=s', "Anything before this will be closed with no email"], + ['email-cutoff=s', "Anything before this will be closed with an email sent to the reporter"], + ['limit|l=s', "limit to a certain number of reports/users to be closed"], + ['help|h', "print usage message and exit" ], +); +print($usage->text), exit if $opts->help; + +FixMyStreet::Script::ArchiveOldEnquiries::archive($opts); diff --git a/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm b/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm new file mode 100644 index 000000000..5d1d45379 --- /dev/null +++ b/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm @@ -0,0 +1,141 @@ +package FixMyStreet::Script::ArchiveOldEnquiries; + +use strict; +use warnings; +require 5.8.0; + +use FixMyStreet; +use FixMyStreet::App; +use FixMyStreet::DB; +use FixMyStreet::Cobrand; +use FixMyStreet::Map; +use FixMyStreet::Email; + + +my $opts = { + commit => 0, + body => '2237', + cobrand => 'oxfordshire', + closure_cutoff => "2015-01-01 00:00:00", + email_cutoff => "2016-01-01 00:00:00", +}; + +sub query { + return { + bodies_str => { 'LIKE', "%".$opts->{body}."%"}, + -and => [ + lastupdate => { '<', $opts->{email_cutoff} }, + lastupdate => { '>', $opts->{closure_cutoff} }, + ], + state => [ FixMyStreet::DB::Result::Problem->open_states() ], + }; +} + +sub archive { + my $params = shift; + if ( $params ) { + $opts = { + %$opts, + %$params, + }; + } + + unless ( $opts->{commit} ) { + printf "Doing a dry run; emails won't be sent and reports won't be closed.\n"; + printf "Re-run with --commit to actually archive reports.\n\n"; + } + + my @user_ids = FixMyStreet::DB->resultset('Problem')->search(query(), + { + distinct => 1, + columns => ['user_id'], + rows => $opts->{limit}, + })->all; + + @user_ids = map { $_->user_id } @user_ids; + + my $users = FixMyStreet::DB->resultset('User')->search({ + id => \@user_ids + }); + + my $user_count = $users->count; + my $problem_count = FixMyStreet::DB->resultset('Problem')->search(query(), + { + columns => ['id'], + rows => $opts->{limit}, + })->count; + + printf("%d users will receive closure emails about %d reports which will be closed.\n", $user_count, $problem_count); + + if ( $opts->{commit} ) { + my $i = 0; + while ( my $user = $users->next ) { + printf("%d/%d: User ID %d\n", ++$i, $user_count, $user->id); + send_email_and_close($user); + } + } + + my $problems_to_close = FixMyStreet::DB->resultset('Problem')->search({ + bodies_str => { 'LIKE', "%".$opts->{body}."%"}, + lastupdate => { '<', $opts->{closure_cutoff} }, + state => [ FixMyStreet::DB::Result::Problem->open_states() ], + }, { + rows => $opts->{limit}, + }); + + printf("Closing %d old reports, without sending emails: ", $problems_to_close->count); + + if ( $opts->{commit} ) { + $problems_to_close->update({ state => 'closed', send_questionnaire => 0 }); + } + + printf("done.\n") +} + +sub send_email_and_close { + my ($user) = @_; + + my $problems = $user->problems->search(query(), { + order_by => { -desc => 'confirmed' }, + }); + + my @problems = $problems->all; + + return if scalar(@problems) == 0; + + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($opts->{cobrand})->new(); + $cobrand->set_lang_and_domain($problems[0]->lang, 1); + FixMyStreet::Map::set_map_class($cobrand->map_type); + + my %h = ( + reports => [@problems], + report_count => scalar(@problems), + site_name => $cobrand->moniker, + user => $user, + cobrand => $cobrand, + ); + + # Send email + printf(" Sending email about %d reports: ", scalar(@problems)); + my $email_error = FixMyStreet::Email::send_cron( + $problems->result_source->schema, + 'archive.txt', + \%h, + { + To => [ [ $user->email, $user->name ] ], + }, + undef, + undef, + $cobrand, + $problems[0]->lang, + ); + + unless ( $email_error ) { + printf("done.\n Closing reports: "); + + $problems->update({ state => 'closed', send_questionnaire => 0 }); + printf("done.\n"); + } else { + printf("error! Not closing reports for this user.\n") + } +} diff --git a/t/app/script/archive_old_enquiries.t b/t/app/script/archive_old_enquiries.t new file mode 100644 index 000000000..e87d6a0f8 --- /dev/null +++ b/t/app/script/archive_old_enquiries.t @@ -0,0 +1,163 @@ +use strict; +use warnings; +use Test::More; +use FixMyStreet::TestMech; +use FixMyStreet::Script::ArchiveOldEnquiries; + +mySociety::Locale::gettext_domain( 'FixMyStreet' ); + +my $mech = FixMyStreet::TestMech->new(); + +$mech->clear_emails_ok; + +my $opts = { + commit => 1, +}; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +my $west_oxon = $mech->create_body_ok(2420, 'West Oxfordshire District Council', id => 2420); + +subtest 'sets reports to the correct status' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test', { + areas => ',2237,', + user_id => $user->id, + }); + + my ($report1) = $mech->create_problems_for_body(1, $oxfordshire->id . "," .$west_oxon->id, 'Test', { + areas => ',2237,', + lastupdate => '2015-12-01 07:00:00', + user => $user, + }); + + my ($report2) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test 2', { + areas => ',2237,', + lastupdate => '2015-12-01 08:00:00', + user => $user, + state => 'investigating', + }); + + my ($report3, $report4) = $mech->create_problems_for_body(2, $oxfordshire->id, 'Test', { + areas => ',2237,', + lastupdate => '2014-12-01 07:00:00', + user => $user, + }); + + my ($report5) = $mech->create_problems_for_body(1, $oxfordshire->id . "," .$west_oxon->id, 'Test', { + areas => ',2237,', + lastupdate => '2014-12-01 07:00:00', + user => $user, + state => 'in progress' + }); + + FixMyStreet::Script::ArchiveOldEnquiries::archive($opts); + + $report->discard_changes; + $report1->discard_changes; + $report2->discard_changes; + $report3->discard_changes; + $report4->discard_changes; + $report5->discard_changes; + + is $report1->state, 'closed', 'Report 1 has been set to closed'; + is $report2->state, 'closed', 'Report 2 has been set to closed'; + is $report3->state, 'closed', 'Report 3 has been set to closed'; + is $report4->state, 'closed', 'Report 4 has been set to closed'; + is $report5->state, 'closed', 'Report 5 has been set to closed'; + + is $report->state, 'confirmed', 'Recent report has been left alone'; + }; +}; + +subtest 'sends emails to a user' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->clear_emails_ok; + $mech->email_count_is(0); + + $mech->create_problems_for_body(1, $oxfordshire->id, 'Shiny new report', { + areas => ',2237,', + user => $user, + }); + + $mech->create_problems_for_body(1, $oxfordshire->id, 'Problem the first', { + areas => ',2237,', + lastupdate => '2015-12-01 07:00:00', + user => $user, + }); + + $mech->create_problems_for_body(1, $oxfordshire->id, 'Problem the second', { + areas => ',2237,', + lastupdate => '2015-12-01 07:00:00', + user => $user, + }); + + $mech->create_problems_for_body(1, $oxfordshire->id, 'Problem the third', { + areas => ',2237,', + lastupdate => '2015-12-01 07:00:00', + user => $user, + }); + + $mech->create_problems_for_body(1, $oxfordshire->id, 'Really old report', { + areas => ',2237,', + lastupdate => '2014-12-01 07:00:00', + user => $user, + }); + + FixMyStreet::Script::ArchiveOldEnquiries::archive($opts); + + my @emails = $mech->get_email; + $mech->email_count_is(1); + + my $email = $emails[0]; + my $body = $mech->get_text_body_from_email($email); + + like $body, qr/Problem the first/, 'Email body matches report name'; + like $body, qr/Problem the second/, 'Email body matches report name'; + like $body, qr/Problem the third/, 'Email body matches report name'; + + unlike $body, qr/Shiny new report/, 'Email body does not have new report'; + unlike $body, qr/Really old report/, 'Email body does not have old report'; + }; +}; + +subtest 'user with old reports does not get email' => sub { + $mech->clear_emails_ok; + $mech->email_count_is(0); + + $mech->create_problems_for_body(4, $oxfordshire->id, 'Really old report', { + areas => ',2237,', + lastupdate => '2014-12-01 07:00:00', + user => $user, + }); + + FixMyStreet::Script::ArchiveOldEnquiries::archive($opts); + + my @emails = $mech->get_email; + $mech->email_count_is(0); +}; + +subtest 'user with new reports does not get email' => sub { + $mech->clear_emails_ok; + $mech->email_count_is(0); + + $mech->create_problems_for_body(4, $oxfordshire->id, 'Shiny new report', { + areas => ',2237,', + user => $user, + }); + + FixMyStreet::Script::ArchiveOldEnquiries::archive($opts); + + $mech->email_count_is(0); +}; + +done_testing(); + +END { + $mech->delete_user($user); + $mech->delete_body($oxfordshire); +} diff --git a/templates/email/oxfordshire/archive.html b/templates/email/oxfordshire/archive.html new file mode 100644 index 000000000..ed48456a2 --- /dev/null +++ b/templates/email/oxfordshire/archive.html @@ -0,0 +1,58 @@ +[% + +email_summary = "Your reports on " _ site_name; + +PROCESS '_email_settings.html'; + +INCLUDE '_email_top.html'; + +%] + + +

Your [% site_name %] reports on FixMyStreet

+

+ Hello [% user.name %], +

+

+ FixMyStreet is being updated in Oxfordshire to + improve how problems get fixed. +

+

+ As part of these updates, we are closing old reports that appear to be + resolved but remain open in the system. +

+

+ We noticed that you have [% report_count %] old [% nget('report', 'reports', report_count) %] on the system, + which we've listed below. +

+

+ If your report is no longer an issue, you don't need to do anything. +

+

+ If you believe that your report is still a problem, you can reopen it by + clicking the 'reopen' button by a report. +

+ + [% FOR report IN reports %] +
+ [% IF report.photo %] + + + + [% END %] +

+ [%~ report.title | html ~%] +

+

[% report.detail | html %]

+

+ Reported [% report.time_ago %] ago. +

+

+ Reopen report +

+
+ [% END %] + + + +[% INCLUDE '_email_bottom.html' %] diff --git a/templates/email/oxfordshire/archive.txt b/templates/email/oxfordshire/archive.txt new file mode 100644 index 000000000..6ecf5b02f --- /dev/null +++ b/templates/email/oxfordshire/archive.txt @@ -0,0 +1,28 @@ +Subject: Your reports on [% site_name %] + +Hello [% user.name %], + +FixMyStreet is being updated in Oxfordshire to improve how problems get fixed. + +As part of these updates, we are closing old reports that appear to be resolved but remain open in the system. + +We noticed that you have [% report_count %] old [% nget('report', 'reports', report_count) %] on the system, which we've listed below. + +If your report is no longer an issue, you don't need to do anything. + +If you believe that your report is still a problem, you can reopen it by clicking or copying and pasting +the link marked 'Reopen?' by a report. + +[% FOR report IN reports %] + +[% report.title %] + +Reported [% report.time_ago %] ago. + +Reopen? [% cobrand.base_url_for_report( report ) %][% report.tokenised_url( user, { reopen => 'true' } ) %]#update_form + +---- + +[% END %] + +The mySociety team and Oxfordshire County Council -- cgit v1.2.3