<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package FixMyStreet::Script::TfL::AutoClose;

use v5.14;

use Moo;
use CronFns;
use FixMyStreet;
use FixMyStreet::Cobrand;
use FixMyStreet::DB;
use Types::Standard qw(InstanceOf Maybe);

has commit =&gt; ( is =&gt; 'ro', default =&gt; 0 );

has verbose =&gt; ( is =&gt; 'ro', default =&gt; 0 );

has body =&gt; (
    is =&gt; 'lazy',
    isa =&gt; Maybe[InstanceOf['FixMyStreet::DB::Result::Body']],
    default =&gt; sub {
        my $body = FixMyStreet::DB-&gt;resultset('Body')-&gt;find({ name =&gt; 'TfL' });
        return $body;
    }
);

has days =&gt; (
    is =&gt; 'ro',
    default =&gt; 28
);

sub close {
    my $self = shift;

    die "Can't find body\n" unless $self-&gt;body;
    warn "DRY RUN: use --commit to close reports\n" unless $self-&gt;commit;
    my $categories = $self-&gt;categories;
    $self-&gt;close_reports($categories);
}

has newest =&gt; (
    is =&gt; 'lazy',
    isa =&gt; InstanceOf['DateTime'],
    default =&gt; sub {
        my $self = shift;
        my $days = $self-&gt;days * -1;
        my $date = DateTime-&gt;now-&gt;add( days =&gt; $days )-&gt;truncate( to =&gt; 'day' );
        return $date;
    }
);

# get list of cateories that have a response template for the fixed
# state marked as auto-response.
sub categories {
    my $self = shift;

    my $templates = FixMyStreet::DB-&gt;resultset('ResponseTemplate')-&gt;search({
        state =&gt; 'fixed - council',
        auto_response =&gt; 1,
        body_id =&gt; $self-&gt;body-&gt;id,
    });

    my %categories;
    for my $template ( $templates-&gt;all ) {
        map { $categories{$_-&gt;category} = $template; } $template-&gt;contacts-&gt;all;
    }

    return \%categories;
}

# find reports in relevant categories that have been set to action
# scheduled for 30 days.
sub close_reports {
    my ($self, $categories) = @_;

    my $dtf = FixMyStreet::DB-&gt;schema-&gt;storage-&gt;datetime_parser;

    my $reports = FixMyStreet::DB-&gt;resultset('Problem')-&gt;search({
        category =&gt; { -in =&gt; [ keys %$categories ] },
        'me.state' =&gt; 'action scheduled',
        bodies_str =&gt; $self-&gt;body-&gt;id,
        'comments.state' =&gt; 'confirmed',
        'comments.problem_state' =&gt; 'action scheduled',
    },
    {
        group_by =&gt; 'me.id',
        join =&gt; [ 'comments' ],
        having =&gt; \[ 'MIN(comments.confirmed) &lt; ?', $dtf-&gt;format_datetime($self-&gt;newest) ]
    });

    my $count = 0;
    for my $r ( $reports-&gt;all ) {
        my $comments = FixMyStreet::DB-&gt;resultset('Comment')-&gt;search(
            { problem_id =&gt; $r-&gt;id },
            { order_by =&gt; 'confirmed' }
        );
        my $old_state = '';
        my $last_change;
        while ( my $c = $comments-&gt;next ) {
            my $new_state = $c-&gt;problem_state || '';
            if ( $new_state eq 'action scheduled' &amp;&amp; $new_state ne $old_state) {
                $last_change = $c-&gt;confirmed;
            }
            $old_state = $new_state if $new_state;
        }
        next unless defined $last_change &amp;&amp; $last_change &lt; $self-&gt;newest;
        if ($self-&gt;commit) {
            $r-&gt;update({
                state =&gt; 'fixed - council',
                lastupdate =&gt; \'current_timestamp',
            });
            my $c = FixMyStreet::DB-&gt;resultset('Comment')-&gt;new(
                {
                    problem =&gt; $r,
                    text =&gt; $categories-&gt;{$r-&gt;category}-&gt;text,
                    state =&gt; 'confirmed',
                    problem_state =&gt; 'fixed - council',
                    user =&gt; $self-&gt;body-&gt;comment_user,
                    confirmed =&gt; \'current_timestamp'
                }
            );
            $c-&gt;insert;
        }
        $count++;
    }

    say "$count reports closed" if $self-&gt;verbose;

    return 1;
}

1;
</pre></body></html>