aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/Open311
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/Open311')
-rw-r--r--perllib/Open311/GetUpdates.pm82
-rw-r--r--perllib/Open311/PopulateServiceList.pm240
2 files changed, 322 insertions, 0 deletions
diff --git a/perllib/Open311/GetUpdates.pm b/perllib/Open311/GetUpdates.pm
new file mode 100644
index 000000000..5d5291d47
--- /dev/null
+++ b/perllib/Open311/GetUpdates.pm
@@ -0,0 +1,82 @@
+package Open311::GetUpdates;
+
+use Moose;
+use Open311;
+use FixMyStreet::App;
+
+has council_list => ( is => 'ro' );
+has system_user => ( is => 'ro' );
+
+sub get_updates {
+ my $self = shift;
+
+ while ( my $council = $self->council_list->next ) {
+ my $open311 = Open311->new(
+ endpoint => $council->endpoint,
+ jurisdiction => $council->jurisdiction,
+ api_key => $council->api_key
+ );
+
+ my $area_id = $council->area_id;
+
+ my $council_details = mySociety::MaPit::call( 'area', $area_id );
+
+ my $reports = FixMyStreet::App->model('DB::Problem')->search(
+ {
+ council => { like => "\%$area_id\%" },
+ state => { 'IN', [qw/confirmed fixed/] },
+ -and => [
+ external_id => { '!=', undef },
+ external_id => { '!=', '' },
+ ],
+ }
+ );
+
+ my @report_ids = ();
+ while ( my $report = $reports->next ) {
+ push @report_ids, $report->external_id;
+ }
+
+ next unless @report_ids;
+
+ $self->update_reports( \@report_ids, $open311, $council_details );
+ }
+}
+
+sub update_reports {
+ my ( $self, $report_ids, $open311, $council_details ) = @_;
+
+ my $service_requests = $open311->get_service_requests( $report_ids );
+
+ my $requests;
+
+ # XML::Simple is a bit inconsistent in how it structures
+ # things depending on the number of children an element has :(
+ if ( ref $service_requests->{request} eq 'ARRAY' ) {
+ $requests = $service_requests->{request};
+ }
+ else {
+ $requests = [ $service_requests->{request} ];
+ }
+
+ for my $request (@$requests) {
+ # if it's a ref that means it's an empty element
+ # however, if there's no updated date then we can't
+ # tell if it's newer that what we have so we should skip it
+ next if ref $request->{updated_datetime} || ! exists $request->{updated_datetime};
+
+ my $request_id = $request->{service_request_id};
+
+ my $problem =
+ FixMyStreet::App->model('DB::Problem')
+ ->search( { external_id => $request_id, } );
+
+ if (my $p = $problem->first) {
+ $p->update_from_open311_service_request( $request, $council_details, $self->system_user );
+ }
+ }
+
+ return 1;
+}
+
+1;
diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm
new file mode 100644
index 000000000..cfec9005d
--- /dev/null
+++ b/perllib/Open311/PopulateServiceList.pm
@@ -0,0 +1,240 @@
+package Open311::PopulateServiceList;
+
+use Moose;
+use LWP::Simple;
+use XML::Simple;
+use FixMyStreet::App;
+use Open311;
+
+has council_list => ( is => 'ro' );
+has found_contacts => ( is => 'rw', default => sub { [] } );
+
+has _current_council => ( is => 'rw' );
+has _current_open311 => ( is => 'rw' );
+has _current_service => ( is => 'rw' );
+
+my $council_list = FixMyStreet::App->model('DB::Open311conf');
+
+sub process_councils {
+ my $self = shift;
+
+ while ( my $council = $self->council_list->next ) {
+ next unless $council->endpoint;
+ $self->_current_council( $council );
+ $self->process_council;
+ }
+}
+
+sub process_council {
+ my $self = shift;
+
+ my $open311 = Open311->new(
+ endpoint => $self->_current_council->endpoint,
+ jurisdiction => $self->_current_council->jurisdiction,
+ api_key => $self->_current_council->api_key
+ );
+
+ $self->_current_open311( $open311 );
+ $self->_check_endpoints;
+
+ my $list = $open311->get_service_list;
+ unless ( $list ) {
+ warn "ERROR: no service list found for " . $self->_current_council->area_id . "\n";
+ return;
+ }
+ $self->process_services( $list );
+}
+
+
+
+sub _check_endpoints {
+ my $self = shift;
+
+ # west berks end point not standard
+ if ( $self->_current_council->area_id == 2619 ) {
+ $self->_current_open311->endpoints(
+ {
+ services => 'Services',
+ requests => 'Requests'
+ }
+ );
+ }
+}
+
+
+sub process_services {
+ my $self = shift;
+ my $list = shift;
+
+ $self->found_contacts( [] );
+ foreach my $service ( @{ $list->{service} } ) {
+ $self->_current_service( $service );
+ $self->process_service;
+ }
+ $self->_delete_contacts_not_in_service_list;
+}
+
+sub process_service {
+ my $self = shift;
+
+ my $category = $self->_current_council->area_id == 2218 ?
+ $self->_current_service->{description} :
+ $self->_current_service->{service_name};
+
+ print $self->_current_service->{service_code} . ': ' . $category . "\n";
+ my $contacts = FixMyStreet::App->model( 'DB::Contact')->search(
+ {
+ area_id => $self->_current_council->area_id,
+ -OR => [
+ email => $self->_current_service->{service_code},
+ category => $category,
+ ]
+ }
+ );
+
+ if ( $contacts->count() > 1 ) {
+ printf(
+ "Multiple contacts for service code %s, category %s - Skipping\n",
+ $self->_current_service->{service_code},
+ $category,
+ );
+
+ # best to not mark them as deleted as we don't know what we're doing
+ while ( my $contact = $contacts->next ) {
+ push @{ $self->found_contacts }, $contact->email;
+ }
+
+ return;
+ }
+
+ my $contact = $contacts->first;
+
+ if ( $contact ) {
+ $self->_handle_existing_contact( $contact );
+ } else {
+ $self->_create_contact;
+ }
+}
+
+sub _handle_existing_contact {
+ my ( $self, $contact ) = @_;
+
+ my $service_name = $self->_normalize_service_name;
+
+ print $self->_current_council->area_id . " already has a contact for service code " . $self->_current_service->{service_code} . "\n";
+
+ if ( $contact->deleted || $service_name ne $contact->category || $self->_current_service->{service_code} ne $contact->email ) {
+ eval {
+ $contact->update(
+ {
+ category => $service_name,
+ email => $self->_current_service->{service_code},
+ confirmed => 1,
+ deleted => 0,
+ editor => $0,
+ whenedited => \'ms_current_timestamp()',
+ note => 'automatically undeleted by script',
+ }
+ );
+ };
+
+ if ( $@ ) {
+ warn "Failed to update contact for service code " . $self->_current_service->{service_code} . " for council @{[$self->_current_council->area_id]}: $@\n";
+ return;
+ }
+ }
+
+ push @{ $self->found_contacts }, $self->_current_service->{service_code};
+}
+
+sub _create_contact {
+ my $self = shift;
+
+ my $service_name = $self->_normalize_service_name;
+
+ my $contact;
+ eval {
+ $contact = FixMyStreet::App->model( 'DB::Contact')->create(
+ {
+ email => $self->_current_service->{service_code},
+ area_id => $self->_current_council->area_id,
+ category => $service_name,
+ confirmed => 1,
+ deleted => 0,
+ editor => $0,
+ whenedited => \'ms_current_timestamp()',
+ note => 'created automatically by script',
+ }
+ );
+ };
+
+ if ( $@ ) {
+ warn "Failed to create contact for service code " . $self->_current_service->{service_code} . " for council @{[$self->_current_council->area_id]}: $@\n";
+ return;
+ }
+
+ if ( $contact and lc( $self->_current_service->{metadata} ) eq 'true' ) {
+ $self->_add_meta_to_contact( $contact );
+ }
+
+ if ( $contact ) {
+ push @{ $self->found_contacts }, $self->_current_service->{service_code};
+ print "created contact for service code " . $self->_current_service->{service_code} . " for council @{[$self->_current_council->area_id]}\n";
+ }
+}
+
+sub _add_contact_to_meta {
+ my ( $self, $contact ) = @_;
+
+ 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} );
+
+ # 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
+ map { $_->{description} =~ s/:\s*//; $_ }
+ # there is a display order and we only want to sort once
+ sort { $a->{order} <=> $b->{order} }
+ @{ $meta_data->{attributes}->{attribute} };
+
+ $contact->extra( \@meta );
+ $contact->update;
+}
+
+sub _normalize_service_name {
+ my $self = shift;
+
+ # FIXME - at the moment it makes more sense to use the description
+ # for cambridgeshire but need a more flexible way to set this
+ my $service_name = $self->_current_council->area_id == 2218 ?
+ $self->_current_service->{description} :
+ $self->_current_service->{service_name};
+ # remove trailing whitespace as it upsets db queries
+ # to look up contact details when creating problem
+ $service_name =~ s/\s+$//;
+
+ return $service_name;
+}
+
+sub _delete_contacts_not_in_service_list {
+ my $self = shift;
+
+ my $found_contacts = FixMyStreet::App->model( 'DB::Contact')->search(
+ {
+ email => { -not_in => $self->found_contacts },
+ area_id => $self->_current_council->area_id,
+ deleted => 0,
+ }
+ );
+
+ $found_contacts->update(
+ {
+ deleted => 1,
+ editor => $0,
+ whenedited => \'ms_current_timestamp()',
+ note => 'automatically marked as deleted by script'
+ }
+ );
+}
+
+1;