diff options
27 files changed, 1353 insertions, 41 deletions
diff --git a/bin/fetch-comments b/bin/fetch-comments new file mode 100755 index 000000000..e0e53826e --- /dev/null +++ b/bin/fetch-comments @@ -0,0 +1,58 @@ +#!/usr/bin/env perl + +# send-reports: +# Send new problem reports to councils +# +# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org. WWW: http://www.mysociety.org + +use strict; +use warnings; +require 5.8.0; + +use Digest::MD5; +use Encode; +use Error qw(:try); +use CronFns; + +use FixMyStreet::App; + +use Utils; +use mySociety::Config; +use mySociety::EmailUtil; + +use Open311; +use Open311::GetServiceRequestUpdates; + +# send_method config values found in by-area config data, for selecting to appropriate method +use constant SEND_METHOD_EMAIL => 'email'; +use constant SEND_METHOD_OPEN311 => 'open311'; + +# Set up site, language etc. +my ( $verbose, $nomail ) = CronFns::options(); +my $base_url = mySociety::Config::get('BASE_URL'); +my $site = CronFns::site($base_url); + +my $councils = FixMyStreet::App->model('DB::Open311Conf')->search( + { + send_method => SEND_METHOD_OPEN311, + send_comments => 1, + comment_user_id => { '!=', undef }, + endpoint => { '!=', '' }, + } +); + +while ( my $council = $councils->next ) { + + my $o = Open311->new( + endpoint => $council->endpoint, + api_key => $council->api_key, + jurisdiction => $council->jurisdiction, + ); + + my $updates = + Open311::GetServiceRequestUpdates->new( + system_user => $council->comment_user ); + + $updates->update_comments( $o, { areaid => $council->area_id }, ); +} diff --git a/bin/send-comments b/bin/send-comments new file mode 100755 index 000000000..df646a682 --- /dev/null +++ b/bin/send-comments @@ -0,0 +1,92 @@ +#!/usr/bin/env perl + +# send-reports: +# Send new problem reports to councils +# +# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org. WWW: http://www.mysociety.org + +use strict; +use warnings; +require 5.8.0; + +use Digest::MD5; +use Encode; +use Error qw(:try); +use CronFns; + +use FixMyStreet::App; + +use Utils; +use mySociety::Config; +use mySociety::EmailUtil; + +use Open311; + +# maximum number of webservice attempts to send before not trying any more (XXX may be better in config?) +use constant SEND_FAIL_RETRIES_CUTOFF => 3; + +# send_method config values found in by-area config data, for selecting to appropriate method +use constant SEND_METHOD_EMAIL => 'email'; +use constant SEND_METHOD_OPEN311 => 'open311'; + +# Set up site, language etc. +my ($verbose, $nomail) = CronFns::options(); +my $base_url = mySociety::Config::get('BASE_URL'); +my $site = CronFns::site($base_url); + +my $councils = FixMyStreet::App->model('DB::Open311Conf')->search( { + send_method => SEND_METHOD_OPEN311, + send_comments => 1, +} ); + +while ( my $council = $councils->next ) { + my $comments = FixMyStreet::App->model('DB::Comment')->search( { + 'me.whensent' => undef, + 'problem.whensent' => { '!=' => undef }, + 'problem.external_id' => { '!=' => undef }, + 'problem.council' => { -like => '%' . $council->area_id .'%' }, + }, + { + join => 'problem', + } + ); + + my $o = Open311->new( + endpoint => $council->endpoint + ); + + while ( my $comment = $comments->next ) { + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($comment->cobrand)->new(); + + if ( $comment->send_fail_count ) { + next if bromley_retry_timeout( $comment ); + } + + my $id = $o->post_service_request_update( $comment ); + + if ( $id ) { + $comment->external_id( $id ); + $comment->update(); + } else { + $comment->update( { + send_fail_count => $comment->send_fail_count + 1, + send_fail_timestamp => \'ms_current_timestamp()', + send_fail_reason => 'Failed to post over Open311', + } ); + } + } +} + +sub bromley_retry_timeout { + my $row = shift; + + my $tz = DateTime::TimeZone->new( name => 'local' ); + my $now = DateTime->now( time_zone => $tz ); + my $diff = $now - $row->send_fail_timestamp; + if ( $diff->minutes < 30 ) { + return 1; + } + + return 0; +} diff --git a/bin/send-reports b/bin/send-reports index 22bd12732..1b93f8c5a 100755 --- a/bin/send-reports +++ b/bin/send-reports @@ -30,6 +30,20 @@ use mySociety::Web qw(ent); use Open311; +# maximum number of webservice attempts to send before not trying any more (XXX may be better in config?) +use constant SEND_FAIL_RETRIES_CUTOFF => 3; + +# specific council numbers +use constant COUNCIL_ID_EAST_HANTS => 2330; + +use constant MAX_LINE_LENGTH => 132; + +# send_method config values found in by-area config data, for selecting to appropriate method +use constant SEND_METHOD_EMAIL => 'email'; +use constant SEND_METHOD_OPEN311 => 'open311'; +use constant SEND_METHOD_EAST_HANTS => 'easthants'; +use constant SEND_METHOD_LONDON => 'london'; + # Set up site, language etc. my ($verbose, $nomail) = CronFns::options(); my $base_url = mySociety::Config::get('BASE_URL'); @@ -40,6 +54,9 @@ my $unsent = FixMyStreet::App->model("DB::Problem")->search( { whensent => undef, council => { '!=', undef }, } ); + +my %sending_skipped_by_method = (); + my (%notgot, %note); while (my $row = $unsent->next) { @@ -56,8 +73,10 @@ while (my $row = $unsent->next) { next; } - my $send_email = 0; - my $send_web = 0; + # Due to multiple councils, it's possible to want to send both by email *and* another method + # NB: might need to revist this if multiple councils have custom send methods + my $send_email = 0; + my $send_method = 0; # Template variables for the email my $email_base_url = $cobrand->base_url_for_emails($row->cobrand_data); @@ -118,6 +137,7 @@ while (my $row = $unsent->next) { push @to, [ $council_email, $name ]; @recips = ($council_email); + $send_method = 0; $send_email = 1; $template = Utils::read_file("$FindBin::Bin/../templates/email/emptyhomes/" . $row->lang . "/submit.txt"); @@ -133,15 +153,37 @@ while (my $row = $unsent->next) { foreach my $council (@councils) { my $name = $areas_info->{$council}->{name}; push @dear, $name; - if ($council == 2330) { # E. Hants have a web service - $send_web = 'easthants'; + + # look in the DB to determine if there is a special handler for this council (e.g., open311, or custom) + my $council_config = FixMyStreet::App->model("DB::Open311conf")->search( { area_id => $council} )->first; + $send_method = $council_config->send_method if $council_config; + if ($council == COUNCIL_ID_EAST_HANTS) { # E. Hants have a web service + $send_method = SEND_METHOD_EAST_HANTS; # TODO: delete? should be in the db $h{category} = 'Customer Services' if $h{category} eq 'Other'; - } elsif ($areas_info->{$council}->{type} eq 'LBO') { # London - $send_web = 'london'; - } elsif ( my $endpoint = FixMyStreet::App->model("DB::Open311conf")->search( { area_id => $council, endpoint => { '!=', '' } } )->first ) { - push @open311_councils, $endpoint; - $send_web = 'open311'; - } else { + } + + # if council lookup provided no explicit send_method, maybe there's some other criterion for setting it: + if (! $send_method) { + if ($areas_info->{$council}->{type} eq 'LBO') { # London + $send_method = SEND_METHOD_LONDON; + } + } + $send_email = 1 unless $send_method; # default to email if nothing explicit was provided + + # currently: open311 without an endpoint is useless, so check the endpoint is set + if ($send_method eq SEND_METHOD_OPEN311) { + if ($council_config->endpoint) { + if ($send_method eq SEND_METHOD_OPEN311) { + push @open311_councils, $council_config; + } + } else { + print "Warning: no endpoint specified in config data for council=$council (will try email instead)\n"; + $send_method = 0; + $send_email = 1; + } + } + + if ($send_email) { my $contact = FixMyStreet::App->model("DB::Contact")->find( { deleted => 0, area_id => $council, @@ -160,7 +202,6 @@ while (my $row = $unsent->next) { } push @to, [ $council_email, $name ]; $recips{$council_email} = 1; - $send_email = 1; } } @recips = keys %recips; @@ -199,15 +240,19 @@ while (my $row = $unsent->next) { } - unless ($send_email || $send_web) { + unless ($send_method) { die 'Report not going anywhere for ID ' . $row->id . '!'; } if (mySociety::Config::get('STAGING_SITE')) { # on a staging server send emails to ourselves rather than the councils - @recips = ( mySociety::Config::get('CONTACT_EMAIL') ); - $send_web = 0; - $send_email = 1; + # ...webservice calls will only go through if explictly allowed here: + my @testing_councils = (); + unless (grep {$row->council eq $_} @testing_councils) { + @recips = ( mySociety::Config::get('CONTACT_EMAIL') ); + $send_method = 0; + $send_email = 1; + } } elsif ($site eq 'emptyhomes') { my $council = $row->council; my $country = $areas_info->{$council}->{country}; @@ -243,20 +288,36 @@ while (my $row = $unsent->next) { ); } - if ($send_web eq 'easthants') { + if ($send_method eq SEND_METHOD_EAST_HANTS) { $h{message} = construct_easthants_message(%h); if (!$nomail) { $result *= post_easthants_message(%h); } - } elsif ($send_web eq 'london') { + } elsif ($send_method eq SEND_METHOD_LONDON) { $h{message} = construct_london_message(%h); if (!$nomail) { $result *= post_london_report( $row, %h ); } - } elsif ($send_web eq 'open311') { + } elsif ($send_method eq SEND_METHOD_OPEN311) { foreach my $conf ( @open311_councils ) { print 'posting to end point for ' . $conf->area_id . "\n" if $verbose; + # Extra bromley fields + if ( $row->council =~ /2482/ ) { + if ( $row->send_fail_count > 0 ) { + next if bromley_retry_timeout( $row ); + } + + my $extra = $row->extra; + push @$extra, { name => 'northing', value => $h{northing} }; + push @$extra, { name => 'easting', value => $h{easting} }; + push @$extra, { name => 'report_url', value => $h{url} }; + push @$extra, { name => 'service_request_id', value => $row->id }; + push @$extra, { name => 'report_title', value => $row->title }; + push @$extra, { name => 'public_anonymity_required', value => $row->anonymous ? 'TRUE' : 'FALSE' }; + $row->extra( $extra ); + } + my $contact = FixMyStreet::App->model("DB::Contact")->find( { deleted => 0, area_id => $conf->area_id, @@ -282,7 +343,7 @@ while (my $row = $unsent->next) { my $resp = $open311->send_service_request( $row, \%h, $contact->email ); # make sure we don't save user changes from above - if ( $row->council =~ /2218/ ) { + if ( $row->council =~ /2218/ or $row->council =~ /2482/ ) { $row->discard_changes(); } @@ -291,6 +352,7 @@ while (my $row = $unsent->next) { $result *= 0; } else { $result *= 1; + update_send_fail_data( $row, 'Open311 failed' ); # temporary fix to resolve some issues with west berks if ( $row->council =~ /2619/ ) { $result *= 0; @@ -316,6 +378,17 @@ if ($verbose) { } } +# not conditional on verbose because these can be considered failures (more relevant than one-off error messages?) +if (keys %sending_skipped_by_method) { + my $c = 0; + print "\nProblem reports that send-reports did not attempt to send because retries >= " . SEND_FAIL_RETRIES_CUTOFF . ":\n"; + foreach my $send_method (sort keys %sending_skipped_by_method) { + printf " %-24s %4d\n", "$send_method:", $sending_skipped_by_method{$send_method}; + $c+=$sending_skipped_by_method{$send_method}; + } + printf " %-24s %4d\n", "Total:", $c; +} + sub _get_district_for_contact { my ( $lat, $lon ) = @_; my $district = @@ -489,3 +562,43 @@ sub london_lookup { return $str; } + +# tests send_fail_count agains cutoff limit +# args: problem (row from problem db) +# returns false if there is no cutoff, otherwise error message +sub does_exceed_cutoff_limit { + my ($problem, $council_name) = @_; + my $err_msg = ""; + if ($problem->send_fail_count >= SEND_FAIL_RETRIES_CUTOFF) { + $sending_skipped_by_method{$council_name || '?'}++; + $council_name &&= " to $council_name"; + $err_msg = "skipped: problem id=" . $problem->id . " send$council_name has failed " + . $problem->send_fail_count . " times, cutoff is " . SEND_FAIL_RETRIES_CUTOFF; + } + return $err_msg; +} + +# update_send_fail_data records the failure (of a webservice send) +# args: problem (row from problem db) +# returns: no return value (updates record) +sub update_send_fail_data { + my ($problem, $err_msg) = @_; + $problem->update( { + send_fail_count => $problem->send_fail_count + 1, + send_fail_timestamp => \'ms_current_timestamp()', + send_fail_reason => $err_msg + } ); +} + +sub bromley_retry_timeout { + my $row = shift; + + my $tz = DateTime::TimeZone->new( name => 'local' ); + my $now = DateTime->now( time_zone => $tz ); + my $diff = $now - $row->send_fail_timestamp; + if ( $diff->minutes < 30 ) { + return 1; + } + + return 0; +} diff --git a/db/schema.sql b/db/schema.sql index 395d1c07b..169939315 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -189,7 +189,12 @@ create table problem ( send_questionnaire boolean not null default 't', extra text, -- extra fields required for open311 flagged boolean not null default 'f', - geocode bytea + geocode bytea, + + -- logging sending failures (used by webservices) + send_fail_count integer not null default 0, + send_fail_reason text, + send_fail_timestamp timestamp ); create index problem_state_latitude_longitude_idx on problem(state, latitude, longitude); create index problem_user_id_idx on problem ( user_id ); @@ -303,9 +308,15 @@ create table comment ( or problem_state = 'fixed' or problem_state = 'fixed - council' or problem_state = 'fixed - user' - ) + ), -- other fields? one to indicate whether this was written by the council -- and should be highlighted in the display? + external_id text, + extra text, + send_fail_count integer not null default 0, + send_fail_reason text, + send_fail_timestamp timestamp, + whensent timestamp ); create index comment_user_id_idx on comment(user_id); @@ -419,5 +430,8 @@ create table open311conf ( area_id integer not null unique, endpoint text not null, jurisdiction text, - api_key text + api_key text, + send_method text, + send_comments boolean not null default 'f', + comment_user_id int references users(id) ); diff --git a/db/schema_0013-add_external_id_to_comment.sql b/db/schema_0013-add_external_id_to_comment.sql new file mode 100644 index 000000000..a1cd11c3f --- /dev/null +++ b/db/schema_0013-add_external_id_to_comment.sql @@ -0,0 +1,6 @@ +begin; + +ALTER TABLE comment + ADD COLUMN external_id TEXT; + +commit; diff --git a/db/schema_0013-add_send_method_column_to_open311conf.sql b/db/schema_0013-add_send_method_column_to_open311conf.sql new file mode 100644 index 000000000..516fdd698 --- /dev/null +++ b/db/schema_0013-add_send_method_column_to_open311conf.sql @@ -0,0 +1,7 @@ + +begin; + +ALTER table open311conf + ADD column send_method TEXT; + +commit; diff --git a/db/schema_0014-add_send_fail_columns_to_problem.sql b/db/schema_0014-add_send_fail_columns_to_problem.sql new file mode 100644 index 000000000..369c4118d --- /dev/null +++ b/db/schema_0014-add_send_fail_columns_to_problem.sql @@ -0,0 +1,10 @@ +begin; + +ALTER table problem + ADD column send_fail_count integer not null default 0; +ALTER table problem + ADD column send_fail_reason text; +ALTER table problem + ADD column send_fail_timestamp timestamp; + +commit; diff --git a/db/schema_0015-add_extra_to_comment.sql b/db/schema_0015-add_extra_to_comment.sql new file mode 100644 index 000000000..5c1789ea2 --- /dev/null +++ b/db/schema_0015-add_extra_to_comment.sql @@ -0,0 +1,6 @@ +begin; + +ALTER TABLE comment + ADD COLUMN extra TEXT; + +commit; diff --git a/db/schema_0016-add_whensent_and_send_fail_to_comment.sql b/db/schema_0016-add_whensent_and_send_fail_to_comment.sql new file mode 100644 index 000000000..792ff2e38 --- /dev/null +++ b/db/schema_0016-add_whensent_and_send_fail_to_comment.sql @@ -0,0 +1,11 @@ +begin; + +ALTER table comment + ADD column send_fail_count integer not null default 0; +ALTER table comment + ADD column send_fail_reason text; +ALTER table comment + ADD column send_fail_timestamp timestamp; +ALTER table comment + ADD column whensent timestamp; +commit; diff --git a/db/schema_0017-add_send_comments_to_open311conf.sql b/db/schema_0017-add_send_comments_to_open311conf.sql new file mode 100644 index 000000000..0ee015575 --- /dev/null +++ b/db/schema_0017-add_send_comments_to_open311conf.sql @@ -0,0 +1,6 @@ +begin; + +ALTER table open311conf + ADD column send_comments BOOL NOT NULL DEFAULT 'f'; + +commit; diff --git a/db/schema_0018-add_comment_user_to_open311conf.sql b/db/schema_0018-add_comment_user_to_open311conf.sql new file mode 100644 index 000000000..93851e2a3 --- /dev/null +++ b/db/schema_0018-add_comment_user_to_open311conf.sql @@ -0,0 +1,6 @@ +begin; + +ALTER TABLE open311conf + ADD COLUMN comment_user_id INT REFERENCES users(id); + +commit; diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index af4cdd5aa..68a30ca2c 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -124,12 +124,14 @@ sub report_form_ajax : Path('ajax') : Args(0) { my $category = $c->render_fragment( 'report/new/category.html'); my $councils_text = $c->render_fragment( 'report/new/councils_text.html'); my $has_open311 = keys %{ $c->stash->{category_extras} }; + my $extra_name_info = $c->render_fragment('report/new/extra_name.html'); my $body = JSON->new->utf8(1)->encode( { councils_text => $councils_text, category => $category, has_open311 => $has_open311, + extra_name_info => $extra_name_info, } ); @@ -542,7 +544,7 @@ sub setup_categories_and_councils : Private { ); $category_label = _('Property type:'); - } elsif ($first_council->{type} eq 'LBO') { + } elsif ($first_council->{id} != 2482 && $first_council->{type} eq 'LBO') { $area_ids_to_list{ $first_council->{id} } = 1; @category_options = ( @@ -589,6 +591,7 @@ sub setup_categories_and_councils : Private { $c->stash->{category_options} = \@category_options; $c->stash->{category_extras} = \%category_extras; $c->stash->{category_extras_json} = encode_json \%category_extras; + $c->stash->{extra_name_info} = $first_council->{id} == 2482 ? 1 : 0; my @missing_details_councils = grep { !$area_ids_to_list{$_} } # @@ -733,7 +736,7 @@ sub process_report : Private { $councils = join( ',', @{ $c->stash->{area_ids_to_list} } ) || -1; $report->council( $councils ); - } elsif ( $first_council->{type} eq 'LBO') { + } elsif ( $first_council->{id} != 2482 && $first_council->{type} eq 'LBO') { unless ( Utils::london_categories()->{ $report->category } ) { $c->stash->{field_errors}->{category} = _('Please choose a category'); @@ -785,6 +788,16 @@ sub process_report : Private { }; } + if ( $contacts[0]->area_id == 2482 ) { + for my $field ( qw/ fms_extra_title / ) { + push @extra, { + name => $field, + description => uc( $field), + value => $c->request->param( $field ) || '', + }; + } + } + if ( @extra ) { $c->stash->{report_meta} = \@extra; $report->extra( \@extra ); @@ -916,6 +929,8 @@ sub check_for_errors : Private { %{ $c->stash->{report}->check_for_errors }, ); + # FIXME: need to check for required bromley fields here + # if they're got the login details wrong when signing in then # we don't care about the name field even though it's validated # by the user object diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 15444f556..772a0b8ee 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -185,6 +185,13 @@ sub process_update : Private { $update->problem_state( $params{state} ); } + if ( $c->req->param('fms_extra_title') ) { + my %extras = (); + $extras{title} = $c->req->param('fms_extra_title'); + $extras{email_alerts_required} = $c->req->param('add_alert'); + $update->extra( \%extras ); + } + $c->stash->{update} = $update; $c->stash->{add_alert} = $c->req->param('add_alert'); diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 195fe4019..d31bf5510 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -1,3 +1,4 @@ +use utf8; package FixMyStreet::DB::Result::Comment; # Created by DBIx::Class::Schema::Loader @@ -7,7 +8,6 @@ use strict; use warnings; use base 'DBIx::Class::Core'; - __PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn"); __PACKAGE__->table("comment"); __PACKAGE__->add_columns( @@ -54,29 +54,62 @@ __PACKAGE__->add_columns( { data_type => "boolean", is_nullable => 0 }, "problem_state", { data_type => "text", is_nullable => 1 }, + "external_id", + { data_type => "text", is_nullable => 1 }, + "extra", + { data_type => "text", is_nullable => 1 }, + "send_fail_count", + { data_type => "integer", default_value => 0, is_nullable => 0 }, + "send_fail_reason", + { data_type => "text", is_nullable => 1 }, + "send_fail_timestamp", + { data_type => "timestamp", is_nullable => 1 }, + "whensent", + { data_type => "timestamp", is_nullable => 1 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to( - "user", - "FixMyStreet::DB::Result::User", - { id => "user_id" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, -); -__PACKAGE__->belongs_to( "problem", "FixMyStreet::DB::Result::Problem", { id => "problem_id" }, { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, ); +__PACKAGE__->belongs_to( + "user", + "FixMyStreet::DB::Result::User", + { id => "user_id" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +); -# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-27 10:07:32 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ilLn3dlagg5COdpZDmzrVQ +# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-26 15:44:18 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nvkElEgSU6XcLd9znSqhmQ + +__PACKAGE__->filter_column( + extra => { + filter_from_storage => sub { + my $self = shift; + my $ser = shift; + return undef unless defined $ser; + my $h = new IO::String($ser); + return RABX::wire_rd($h); + }, + filter_to_storage => sub { + my $self = shift; + my $data = shift; + my $ser = ''; + my $h = new IO::String($ser); + RABX::wire_wr( $data, $h ); + return $ser; + }, + } +); use DateTime::TimeZone; use Image::Size; use Moose; use namespace::clean -except => [ 'meta' ]; +use RABX; with 'FixMyStreet::Roles::Abuser'; diff --git a/perllib/FixMyStreet/DB/Result/Open311conf.pm b/perllib/FixMyStreet/DB/Result/Open311conf.pm index 0a5784560..b9cd432ee 100644 --- a/perllib/FixMyStreet/DB/Result/Open311conf.pm +++ b/perllib/FixMyStreet/DB/Result/Open311conf.pm @@ -1,3 +1,4 @@ +use utf8; package FixMyStreet::DB::Result::Open311conf; # Created by DBIx::Class::Schema::Loader @@ -7,7 +8,6 @@ use strict; use warnings; use base 'DBIx::Class::Core'; - __PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime", "EncodedColumn"); __PACKAGE__->table("open311conf"); __PACKAGE__->add_columns( @@ -26,13 +26,30 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 1 }, "api_key", { data_type => "text", is_nullable => 1 }, + "send_method", + { data_type => "text", is_nullable => 1 }, + "send_comments", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, + "comment_user_id", + { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->add_unique_constraint("open311conf_area_id_key", ["area_id"]); +__PACKAGE__->belongs_to( + "comment_user", + "FixMyStreet::DB::Result::User", + { id => "comment_user_id" }, + { + is_deferrable => 1, + join_type => "LEFT", + on_delete => "CASCADE", + on_update => "CASCADE", + }, +); -# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-07-29 18:09:25 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ryqCpvwjNtQrZm4I3s0hxg +# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-26 17:03:34 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fC6Ws8p/pXyjgqfm2LRKsw # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index ce7488703..8c479953b 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -84,6 +84,12 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 0 }, "geocode", { data_type => "bytea", is_nullable => 1 }, + "send_fail_count", + { data_type => "integer", is_nullable => 1 }, + "send_fail_reason", + { data_type => "text", is_nullable => 1 }, + "send_fail_timestamp", + { data_type => "timestamp", is_nullable => 1 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->has_many( @@ -106,8 +112,8 @@ __PACKAGE__->has_many( ); -# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-09-19 14:38:43 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nq8Ufn/SEoDGSrrGlHIxag +# Created by DBIx::Class::Schema::Loader v0.07017 @ 2012-03-16 10:08:56 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:VODeZlWk8l/+IzBBlRNV0A # Add fake relationship to stored procedure table __PACKAGE__->has_one( diff --git a/perllib/Open311.pm b/perllib/Open311.pm index e26e3e4c6..50c7abb54 100644 --- a/perllib/Open311.pm +++ b/perllib/Open311.pm @@ -12,8 +12,9 @@ has api_key => ( is => 'ro', isa => 'Str' ); has endpoint => ( is => 'ro', isa => 'Str' ); has test_mode => ( is => 'ro', isa => 'Bool' ); has test_uri_used => ( is => 'rw', 'isa' => 'Str' ); +has test_req_used => ( is => 'rw' ); has test_get_returns => ( is => 'rw' ); -has endpoints => ( is => 'rw', default => sub { { services => 'services.xml', requests => 'requests.xml' } } ); +has endpoints => ( is => 'rw', default => sub { { services => 'services.xml', requests => 'requests.xml', service_request_updates => 'update.xml', update => 'update.xml' } } ); has debug => ( is => 'ro', isa => 'Bool', default => 0 ); has debug_details => ( is => 'rw', 'isa' => 'Str', default => '' ); @@ -74,7 +75,9 @@ EOT my $extras = $problem->extra; for my $attr ( @$extras ) { - my $name = sprintf( 'attribute[%s]', $attr->{name} ); + my $attr_name = $attr->{name}; + $attr_name =~ s/fms_extra_//; + my $name = sprintf( 'attribute[%s]', $attr_name ); $params->{ $name } = $attr->{value}; } } @@ -129,6 +132,72 @@ sub get_service_request_id_from_token { } } +sub get_service_request_updates { + my $self = shift; + + my $params = {}; + + my $xml = $self->_get( $self->endpoints->{service_request_updates}, $params || undef ); + my $service_requests = $self->_get_xml_object( $xml ); + my $requests; + if ( ref $service_requests->{request_update } eq 'ARRAY' ) { + $requests = $service_requests->{request_update}; + } + else { + $requests = [ $service_requests->{request_update} ]; + } + + return $requests; +} + +sub post_service_request_update { + my $self = shift; + my $comment = shift; + + my $params = { + update_id_ext => $comment->id, + updated_datetime => $comment->confirmed, + service_request_id => $comment->problem->external_id, + service_request_id_ext => $comment->problem->id, + status => $comment->problem->is_open ? 'OPEN' : 'CLOSED', + email => $comment->user->email, + description => $comment->text, + public_anonymity_required => $comment->anonymous ? 'TRUE' : 'FALSE', + # also need last_name, title + }; + + if ( $comment->extra ) { + $params->{'email_alerts_request'} + = $comment->extra->{email_alerts_requested} ? 'TRUE' : 'FALSE'; + $params->{'title'} = $comment->extra->{title}; + } + + my $response = $self->_post( $self->endpoints->{update}, $params ); + + if ( $response ) { + my $obj = $self->_get_xml_object( $response ); + + if ( $obj ) { + if ( $obj->{ request_update }->{ update_id } ) { + my $update_id = $obj->{request_update}->{update_id}; + + # if there's nothing in the update_id element we get a HASHREF back + unless ( ref $update_id ) { + return $obj->{ request_update }->{ update_id }; + } + } else { + my $token = $obj->{ request_update }->{ token }; + if ( $token ) { + return $self->get_service_request_id_from_token( $token ); + } + } + } + + warn sprintf( "Failed to submit comment %s over Open311, response - %s\n%s", $comment->id, $response, $self->debug_details ); + return 0; + } +} + sub _get { my $self = shift; my $path = shift; @@ -171,7 +240,14 @@ sub _post { $self->debug_details( $self->debug_details . "\nrequest:" . $req->as_string ); my $ua = LWP::UserAgent->new(); - my $res = $ua->request( $req ); + my $res; + + if ( $self->test_mode ) { + $res = $self->test_get_returns->{ $path }; + $self->test_req_used( $req ); + } else { + $res = $ua->request( $req ); + } if ( $res->is_success ) { return $res->decoded_content; diff --git a/perllib/Open311/GetServiceRequestUpdates.pm b/perllib/Open311/GetServiceRequestUpdates.pm new file mode 100644 index 000000000..dc8fb7672 --- /dev/null +++ b/perllib/Open311/GetServiceRequestUpdates.pm @@ -0,0 +1,68 @@ +package Open311::GetServiceRequestUpdates; + +use Moose; +use Open311; +use FixMyStreet::App; + +has council_list => ( is => 'ro' ); +has system_user => ( is => 'ro' ); + +sub update_comments { + my ( $self, $open311, $council_details ) = @_; + + my $requests = $open311->get_service_request_updates( ); + + for my $request (@$requests) { + my $request_id = $request->{service_request_id}; + + # If there's no request id then we can't work out + # what problem it belongs to so just skip + next unless $request_id; + + my $problem = + FixMyStreet::App->model('DB::Problem') + ->search( { + external_id => $request_id, + council => { like => '%' . $council_details->{areaid} . '%' }, + } ); + + if (my $p = $problem->first) { + my $c = $p->comments->search( { external_id => $request->{update_id} } ); + + if ( !$c->first ) { + my $comment = FixMyStreet::App->model('DB::Comment')->new( + { + problem => $p, + user => $self->system_user, + external_id => $request->{update_id}, + text => $request->{description}, + mark_fixed => 0, + mark_open => 0, + anonymous => 0, + name => $self->system_user->name + } + ); + $comment->confirm; + + + if ( $p->is_open and $request->{status} eq 'closed' ) { + $p->state( 'fixed - council' ); + $p->update; + + $comment->mark_fixed( 1 ); + } elsif ( ( $p->is_closed || $p->is_fixed ) and $request->{status} eq 'open' ) { + $p->state( 'confirmed' ); + $p->update; + + $comment->mark_open( 1 ); + } + + $comment->insert(); + } + } + } + + return 1; +} + +1; diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm index cfec9005d..ac355a33c 100644 --- a/perllib/Open311/PopulateServiceList.pm +++ b/perllib/Open311/PopulateServiceList.pm @@ -189,6 +189,12 @@ sub _add_contact_to_meta { print "Fetching meta data for $self->_current_service->{service_code}\n"; my $meta_data = $self->_current_open311->get_service_meta_info( $self->_current_service->{service_code} ); + if ( ref $meta_data->{ attributes }->{ attribute } eq 'HASH' ) { + $meta_data->{ attributes }->{ attribute } = [ + $meta_data->{ attributes }->{ attribute } + ]; + } + # turn the data into something a bit more friendly to use my @meta = # remove trailing colon as we add this when we display so we don't want 2 @@ -197,6 +203,26 @@ sub _add_contact_to_meta { sort { $a->{order} <=> $b->{order} } @{ $meta_data->{attributes}->{attribute} }; + # we add these later on from bromley so don't list them here + # as we don't want to display them + if ( $self->_current_council->area_id == 2482 ) { + my %ignore = map { $_ => 1 } qw/ + service_request_id_ext + requested_datetime + report_url + title + last_name + email + easting + northing + report_title + public_anonymity_required + email_alerts_requested + /; + + @meta = grep { ! $ignore{ $_->{ code } } } @meta; + } + $contact->extra( \@meta ); $contact->update; } diff --git a/t/open311.t b/t/open311.t index 30de330b6..2a9d513eb 100644 --- a/t/open311.t +++ b/t/open311.t @@ -5,6 +5,9 @@ use warnings; use Test::More; use Test::Warn; use FixMyStreet::App; +use CGI::Simple; +use HTTP::Response; +use DateTime; use FindBin; use lib "$FindBin::Bin/../perllib"; @@ -39,4 +42,145 @@ my $expected_error = qr{.*request failed: 500 Can.t connect to 192.168.50.1:80 \ warning_like {$o2->send_service_request( $p, { url => 'http://example.com/' }, 1 )} $expected_error, 'warning generated on failed call'; +my $dt = DateTime->now(); + +my $user = FixMyStreet::App->model('DB::User')->new( { + email => 'test@example.com', +} ); + +my $problem = FixMyStreet::App->model('DB::Problem')->new( { + id => 80, + external_id => 81, + state => 'confirmed', +} ); + +my $comment = FixMyStreet::App->model('DB::Comment')->new( { + id => 38362, + user => $user, + problem => $problem, + anonymous => 0, + text => 'this is a comment', + confirmed => $dt, +} ); + +subtest 'testing posting updates' => sub { + my $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' ); + + is $results->{ res }, 248, 'got update id'; + + my $req = $o->test_req_used; + + my $c = CGI::Simple->new( $results->{ req }->content ); + + is $c->param('description'), 'this is a comment', 'email correct'; + is $c->param('email'), 'test@example.com', 'email correct'; + is $c->param('status'), 'OPEN', 'status correct'; + is $c->param('service_request_id_ext'), 80, 'external request id correct'; + is $c->param('service_request_id'), 81, 'request id correct'; + is $c->param('public_anonymity_required'), 'FALSE', 'anon status correct'; + is $c->param('updated_datetime'), $dt, 'correct date'; +}; + +foreach my $test ( + { + desc => 'fixed is CLOSED', + state => 'fixed', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'fixed - user is CLOSED', + state => 'fixed - user', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'fixed - council is CLOSED', + state => 'fixed - council', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'closed is CLOSED', + state => 'closed', + anon => 0, + status => 'CLOSED', + }, + { + desc => 'investigating is OPEN', + state => 'investigating', + anon => 0, + status => 'OPEN', + }, + { + desc => 'planned is OPEN', + state => 'planned', + anon => 0, + status => 'OPEN', + }, + { + desc => 'in progress is OPEN', + state => 'in progress', + anon => 0, + status => 'OPEN', + }, + { + desc => 'anonymous set to true', + state => 'confirmed', + anon => 1, + status => 'OPEN', + }, +) { + subtest $test->{desc} => sub { + $comment->problem->state( $test->{state} ); + $comment->anonymous( $test->{anon} ); + + my $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>' ); + + my $c = CGI::Simple->new( $results->{ req }->content ); + is $c->param('status'), $test->{status}, 'correct status'; + is $c->param('public_anonymity_required'), $test->{anon} ? 'TRUE' : 'FALSE', 'correct anonymity'; + }; +} + +subtest 'No update id in reponse' => sub { + my $results; + warning_like { + $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id></update_id></request_update></service_request_updates>' ) + } qr/Failed to submit comment \d+ over Open311/, 'correct error message'; + + is $results->{ res }, 0, 'No update_id is a failure'; +}; + +subtest 'error reponse' => sub { + my $results; + warning_like { + $results = make_update_req( $comment, '<?xml version="1.0" encoding="utf-8"?><errors><error><code>400</code><description>There was an error</description</error></errors>' ) + } qr/Failed to submit comment \d+ over Open311.*There was an error/, 'correct error messages'; + + is $results->{ res }, 0, 'error in response is a failure'; +}; + done_testing(); + +sub make_update_req { + my $comment = shift; + my $xml = shift; + + my $o = Open311->new( test_mode => 1, end_point => 'http://localhost/o311' ); + + my $test_res = HTTP::Response->new(); + $test_res->code( 200 ); + $test_res->message( 'OK' ); + $test_res->content( $xml ); + + $o->test_get_returns( { + 'update.xml' => $test_res + } ); + + my $res = $o->post_service_request_update( $comment ); + + my $req = $o->test_req_used; + + return { res => $res, req => $req }; +} diff --git a/t/open311/getservicerequestupdates.t b/t/open311/getservicerequestupdates.t new file mode 100644 index 000000000..c9f97cd5a --- /dev/null +++ b/t/open311/getservicerequestupdates.t @@ -0,0 +1,266 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use Test::More; + +use FindBin; +use lib "$FindBin::Bin/../perllib"; +use lib "$FindBin::Bin/../commonlib/perllib"; + +use_ok( 'Open311' ); + +use_ok( 'Open311::GetServiceRequestUpdates' ); +use DateTime; +use FixMyStreet::App; + +my $user = FixMyStreet::App->model('DB::User')->find_or_create( + { + email => 'system_user@example.com' + } +); + +my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> +<service_requests_updates> +<request_update> +<update_id>638344</update_id> +<service_request_id>1</service_request_id> +<service_request_id_ext>1</service_request_id_ext> +<status>open</status> +<description>This is a note</description> +UPDATED_DATETIME +</request_update> +</service_requests_updates> +}; + + +my $dt = DateTime->now; + +# basic xml -> perl object tests +for my $test ( + { + desc => 'basic parsing - element missing', + updated_datetime => '', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note' }, + }, + { + desc => 'basic parsing - empty element', + updated_datetime => '<updated_datetime />', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => {} } , + }, + { + desc => 'basic parsing - element with no content', + updated_datetime => '<updated_datetime></updated_datetime>', + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => {} } , + }, + { + desc => 'basic parsing - element with content', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + res => { update_id => 638344, service_request_id => 1, service_request_id_ext => 1, + status => 'open', description => 'This is a note', updated_datetime => $dt } , + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + my $res = $o->get_service_request_updates; + is_deeply $res->[0], $test->{ res }, 'result looks correct'; + + }; +} + +my $problem_rs = FixMyStreet::App->model('DB::Problem'); +my $problem = $problem_rs->new( + { + postcode => 'EH99 1SP', + latitude => 1, + longitude => 1, + areas => 1, + title => '', + detail => '', + used_map => 1, + user_id => 1, + name => '', + state => 'confirmed', + service => '', + cobrand => 'default', + cobrand_data => '', + user => $user, + created => DateTime->now()->subtract( days => 1 ), + lastupdate => DateTime->now()->subtract( days => 1 ), + anonymous => 1, + external_id => time(), + council => 2482, + } +); + +$problem->insert; + +for my $test ( + { + desc => 'element with content', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'confirmed', + close_comment => 0, + mark_fixed=> 0, + mark_open => 0, + end_state => 'confirmed', + }, + { + desc => 'comment closes report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'confirmed', + close_comment => 1, + mark_fixed=> 1, + mark_open => 0, + end_state => 'fixed - council', + }, + { + desc => 'comment re-opens fixed report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'fixed - user', + close_comment => 0, + mark_fixed => 0, + mark_open => 1, + end_state => 'confirmed', + }, + { + desc => 'comment re-opens closed report', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'closed', + close_comment => 0, + mark_fixed => 0, + mark_open => 1, + end_state => 'confirmed', + }, + { + desc => 'comment leaves report closed', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + description => 'This is a note', + external_id => 638344, + start_state => 'closed', + close_comment => 1, + mark_fixed => 0, + mark_open => 0, + end_state => 'closed', + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + $local_requests_xml =~ s#<service_request_id>\d+</service_request_id>#<service_request_id>@{[$problem->external_id]}</service_request_id>#; + $local_requests_xml =~ s#<service_request_id_ext>\d+</service_request_id_ext>#<service_request_id_ext>@{[$problem->id]}</service_request_id_ext>#; + $local_requests_xml =~ s#<status>\w+</status>#<status>closed</status># if $test->{close_comment}; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + $problem->comments->delete; + $problem->state( $test->{start_state} ); + $problem->update; + + my $council_details = { areaid => 2482 }; + my $update = Open311::GetServiceRequestUpdates->new( system_user => $user ); + $update->update_comments( $o, $council_details ); + + is $problem->comments->count, 1, 'comment count'; + $problem->discard_changes; + + my $c = FixMyStreet::App->model('DB::Comment')->search( { external_id => $test->{external_id} } )->first; + ok $c, 'comment exists'; + is $c->text, $test->{description}, 'text correct'; + is $c->mark_fixed, $test->{mark_fixed}, 'mark_closed correct'; + is $c->mark_open, $test->{mark_open}, 'mark_open correct'; + is $problem->state, $test->{end_state}, 'correct problem state'; + }; +} + +my $problem2 = $problem_rs->new( + { + postcode => 'EH99 1SP', + latitude => 1, + longitude => 1, + areas => 1, + title => '', + detail => '', + used_map => 1, + user_id => 1, + name => '', + state => 'confirmed', + service => '', + cobrand => 'default', + cobrand_data => '', + user => $user, + created => DateTime->now(), + lastupdate => DateTime->now(), + anonymous => 1, + external_id => $problem->external_id, + council => 2651, + } +); + +$problem2->insert(); +$problem->comments->delete; +$problem2->comments->delete; + +for my $test ( + { + desc => 'identical external_ids on problem resolved using council', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + external_id => 638344, + area_id => 2651, + request_id => $problem2->external_id, + request_id_ext => $problem2->id, + p1_comments => 0, + p2_comments => 1, + }, + { + desc => 'identical external_ids on comments resolved', + updated_datetime => sprintf( '<updated_datetime>%s</updated_datetime>', $dt ), + external_id => 638344, + area_id => 2482, + request_id => $problem->external_id, + request_id_ext => $problem->id, + p1_comments => 1, + p2_comments => 1, + }, +) { + subtest $test->{desc} => sub { + my $local_requests_xml = $requests_xml; + $local_requests_xml =~ s/UPDATED_DATETIME/$test->{updated_datetime}/; + $local_requests_xml =~ s#<service_request_id>\d+</service_request_id>#<service_request_id>$test->{request_id}</service_request_id>#; + $local_requests_xml =~ s#<service_request_id_ext>\d+</service_request_id_ext>#<service_request_id_ext>$test->{request_id_ext}</service_request_id_ext>#; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'update.xml' => $local_requests_xml } ); + + + my $council_details = { areaid => $test->{area_id} }; + my $update = Open311::GetServiceRequestUpdates->new( system_user => $user ); + $update->update_comments( $o, $council_details ); + + is $problem->comments->count, $test->{p1_comments}, 'comment count for first problem'; + is $problem2->comments->count, $test->{p2_comments}, 'comment count for second problem'; + }; +} +$problem2->comments->delete(); +$problem->comments->delete(); +$problem2->delete; +$problem->delete; +$user->comments->delete; +$user->problems->delete; +$user->delete; + +done_testing(); diff --git a/t/open311/populate-service-list.t b/t/open311/populate-service-list.t index bdb7404f9..63500d6cb 100644 --- a/t/open311/populate-service-list.t +++ b/t/open311/populate-service-list.t @@ -203,6 +203,304 @@ subtest 'check conflicting contacts not changed' => sub { is $contact_count, 4, 'correct number of contacts'; }; +subtest 'check meta data population' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 2482 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_contact_to_meta( $contact ); + + my $extra = [ { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + } ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'meta data saved'; +}; + +subtest 'check attribute ordering' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>colour</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Colour of bin</datatype_description> + <order>3</order> + <description>Colour of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>size</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Size of bin</datatype_description> + <order>2</order> + <description>Size of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 1 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_contact_to_meta( $contact ); + + my $extra = [ + { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'size', + datatype => 'string', + required => 'true', + datatype_description => 'Size of bin', + order => 2, + description => 'Size of bin' + + }, + { + variable => 'true', + code => 'colour', + datatype => 'string', + required => 'true', + datatype_description => 'Colour of bin', + order => 3, + description => 'Colour of bin' + + }, + ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'meta data re-ordered correctly'; +}; + +subtest 'check bromely skip code' => sub { + my $processor = Open311::PopulateServiceList->new( council_list => [] ); + + my $meta_xml = '<?xml version="1.0" encoding="utf-8"?> +<service_definition> + <service_code>100</service_code> + <attributes> + <attribute> + <variable>true</variable> + <code>type</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>title</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + <attribute> + <variable>true</variable> + <code>report_url</code> + <datatype>string</datatype> + <required>true</required> + <datatype_description>Type of bin</datatype_description> + <order>1</order> + <description>Type of bin</description> + </attribute> + </attributes> +</service_definition> + '; + + my $contact = FixMyStreet::App->model('DB::Contact')->find_or_create( + { + area_id => 1, + email => '001', + category => 'Bins left out 24x7', + confirmed => 1, + deleted => 0, + editor => $0, + whenedited => \'ms_current_timestamp()', + note => 'test contact', + } + ); + + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'services/100.xml' => $meta_xml } + ); + + my $council = FixMyStreet::App->model('DB::Open311conf')->new( { + area_id => 2482 + } ); + + $processor->_current_open311( $o ); + $processor->_current_council( $council ); + $processor->_current_service( { service_code => 100 } ); + + $processor->_add_contact_to_meta( $contact ); + + my $extra = [ { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + } ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'only non std bromley meta data saved'; + + $council->area_id(1); + + $processor->_current_council( $council ); + $processor->_add_contact_to_meta( $contact ); + + $extra = [ + { + variable => 'true', + code => 'type', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'title', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + { + variable => 'true', + code => 'report_url', + datatype => 'string', + required => 'true', + datatype_description => 'Type of bin', + order => 1, + description => 'Type of bin' + + }, + ]; + + $contact->discard_changes; + + is_deeply $contact->extra, $extra, 'all meta data saved for non bromley'; +}; + sub get_standard_xml { return qq{<?xml version="1.0" encoding="utf-8"?> <services> diff --git a/templates/web/default/admin/report_edit.html b/templates/web/default/admin/report_edit.html index cbba1b3b0..9ef7e8248 100644 --- a/templates/web/default/admin/report_edit.html +++ b/templates/web/default/admin/report_edit.html @@ -37,6 +37,7 @@ <li>[% loc('Service:') %] [% problem.service %]</li> <li>[% loc('Cobrand:') %] [% problem.cobrand %]</li> <li>[% loc('Cobrand data:') %] [% problem.cobrand_data %]</li> +<li>[% loc('Extra data:') %] [% problem.extra ? 'Yes' : 'No' %]</li> <li>[% loc('Going to send questionnaire?') %] [% IF problem.send_questionnaire %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</li> <li><label for="flagged">[% loc('Flagged:') %]</label> <input type="checkbox" name="flagged"[% ' checked' IF problem.flagged %]></li> diff --git a/templates/web/fixmystreet/report/display.html b/templates/web/fixmystreet/report/display.html index 0f650b7c2..72c637a5b 100644 --- a/templates/web/fixmystreet/report/display.html +++ b/templates/web/fixmystreet/report/display.html @@ -178,6 +178,10 @@ [% INCLUDE 'footer.html' %] [% BLOCK name %] + [% IF problem.council == '2482' %] + [% extra_name_info = 1 %] + [% INCLUDE 'report/new/extra_name.html' %] + [% END %] <label for="form_name">[% loc('Name') %]</label> [% IF field_errors.name %] <p class='form-error'>[% field_errors.name %]</p> diff --git a/templates/web/fixmystreet/report/new/extra_name.html b/templates/web/fixmystreet/report/new/extra_name.html new file mode 100644 index 000000000..7b057a831 --- /dev/null +++ b/templates/web/fixmystreet/report/new/extra_name.html @@ -0,0 +1,15 @@ +[% IF extra_name_info %] +<label for="form_fms_extra_title">Title</label> +[% IF field_errors.fms_extra_title %] + <p class='form-error'>[% field_errors.fms_extra_title %]</p> +[% END %] +<select class="form-focus-trigger" id="form_fms_extra_title" + name="fms_extra_title" required> + <option></option> + <option value="Mr">Mr</option> + <option value="Miss">Miss</option> + <option value="Mrs">Mrs</option> + <option value="Ms">Ms</option> + <option value="Dr">Dr</option> +</select> +[% END %] diff --git a/templates/web/fixmystreet/report/new/fill_in_details_form.html b/templates/web/fixmystreet/report/new/fill_in_details_form.html index 283f748ba..5ee11fb57 100644 --- a/templates/web/fixmystreet/report/new/fill_in_details_form.html +++ b/templates/web/fixmystreet/report/new/fill_in_details_form.html @@ -168,10 +168,13 @@ <div id="form_sign_in_no" class="form-box"> <h5>[% loc('<strong>No</strong> Let me confirm my report by email') %]</h5> + [% INCLUDE 'report/new/extra_name.html' %] + <label for="form_may_show_nameme">[% loc('Name') %]</label> [% IF field_errors.name %] <p class='form-error'>[% field_errors.name %]</p> [% END %] + <input type="text" class="form-focus-trigger validName" value="[% report.name | html %]" name="name" id="form_name" placeholder="[% loc('Your name') %]"> [%# if there is nothing in the name field then set check box as default on form %] diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 272fd6c12..79740de78 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -440,6 +440,10 @@ OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, { } $('#councils_text').html(data.councils_text); $('#form_category_row').html(data.category); + if ( data.extra_name_info ) { + var lb = $('#form_name').prev(); + lb.before(data.extra_name_info); + } }); $('#side-form, #site-logo').show(); |