#!/usr/bin/env perl # send-comments: # Send comments/updates on reports to bodies # In Open311 parlance these are 'service request updates' and are sent using # mySociety's proposed extension to the Open311 Georeport v2 spec: # https://github.com/mysociety/fixmystreet/wiki/Open311-FMS---Proposed-differences-to-Open311 use strict; use warnings; use v5.14; BEGIN { use File::Basename qw(dirname); use File::Spec; my $d = dirname(File::Spec->rel2abs($0)); require "$d/../setenv.pl"; } use CronFns; use DateTime; use FixMyStreet; use FixMyStreet::Cobrand; use FixMyStreet::DB; use Open311; # 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 COUNCIL_ID_OXFORDSHIRE => 2237; use constant COUNCIL_ID_BROMLEY => 2482; use constant COUNCIL_ID_LEWISHAM => 2492; # Set up site, language etc. my ($verbose, $nomail) = CronFns::options(); my $base_url = FixMyStreet->config('BASE_URL'); my $site = ''; $site = 'fixmystreet.com' if $base_url eq "https://www.fixmystreet.com"; my $bodies = FixMyStreet::DB->resultset('Body')->search( { send_method => SEND_METHOD_OPEN311, send_comments => 1, } ); while ( my $body = $bodies->next ) { # XXX Cobrand specific - see also list in Problem->updates_sent_to_body if ($site eq 'fixmystreet.com') { # Oxfordshire (OCC) is special: # we do *receive* service_request_updates (aka comments) for OCC, but we never *send* them, so skip this pass next if $body->areas->{+COUNCIL_ID_OXFORDSHIRE}; # Lewisham does not yet accept updates next if $body->areas->{+COUNCIL_ID_LEWISHAM}; } my $use_extended = 0; my $comments = FixMyStreet::DB->resultset('Comment')->search( { 'me.whensent' => undef, 'me.external_id' => undef, 'me.state' => 'confirmed', 'me.confirmed' => { '!=' => undef }, 'problem.whensent' => { '!=' => undef }, 'problem.external_id' => { '!=' => undef }, 'problem.bodies_str' => { -like => '%' . $body->id . '%' }, 'problem.send_method_used' => 'Open311', }, { join => 'problem', order_by => [ 'confirmed', 'id' ], } ); if ( $site eq 'fixmystreet.com' && $body->areas->{+COUNCIL_ID_BROMLEY} ) { $use_extended = 1; } my %open311_conf = ( endpoint => $body->endpoint, jurisdiction => $body->jurisdiction, api_key => $body->api_key, use_extended_updates => $use_extended, ); if ( $body->send_extended_statuses ) { $open311_conf{extended_statuses} = 1; } my $o = Open311->new( %open311_conf ); if ( $site eq 'fixmystreet.com' && $body->areas->{+COUNCIL_ID_BROMLEY} ) { my $endpoints = $o->endpoints; $endpoints->{update} = 'update.xml'; $endpoints->{service_request_updates} = 'update.xml'; $o->endpoints( $endpoints ); } while ( my $comment = $comments->next ) { my $cobrand = $body->get_cobrand_handler || FixMyStreet::Cobrand->get_class_for_moniker($comment->cobrand)->new(); # Some cobrands (e.g. Buckinghamshire) don't want to receive updates # from anyone except the original problem reporter. if ($cobrand->call_hook(should_skip_sending_update => $comment)) { unless (defined $comment->get_extra_metadata('cobrand_skipped_sending')) { $comment->set_extra_metadata(cobrand_skipped_sending => 1); $comment->update; } next; } # TODO actually this should be OK for any devolved endpoint if original Open311->can_be_devolved, presumably if ( 0 ) { # Check can_be_devolved and do this properly if set my $sender = $cobrand->get_body_sender( $body, $comment->problem->category ); my $config = $sender->{config}; $o = Open311->new( endpoint => $config->endpoint, jurisdiction => $config->jurisdiction, api_key => $config->api_key, use_extended_updates => 1, # FMB uses extended updates ); } next if !$verbose && $comment->send_fail_count && retry_timeout($comment); if ( $site eq 'fixmystreet.com' && $body->areas->{+COUNCIL_ID_BROMLEY} ) { my $extra = $comment->extra; if ( !$extra ) { $extra = {}; } unless ( $extra->{title} ) { $extra->{title} = $comment->user->title; $comment->extra( $extra ); } } my $id = $o->post_service_request_update( $comment ); if ( $id ) { $comment->update( { external_id => $id, whensent => \'current_timestamp', } ); } else { $comment->update( { send_fail_count => $comment->send_fail_count + 1, send_fail_timestamp => \'current_timestamp', send_fail_reason => "Failed to post over Open311\n\n" . $o->error, } ); if ( $verbose && $o->error ) { warn $o->error; } } } } sub retry_timeout { my $row = shift; my $tz = FixMyStreet->local_time_zone; my $now = DateTime->now( time_zone => $tz ); my $diff = $now - $row->send_fail_timestamp; if ( $diff->in_units( 'minutes' ) < 30 ) { return 1; } return 0; }