From 94932c410290081461eecb2819bc187335452672 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Sun, 19 Apr 2020 16:49:31 +0200 Subject: add commute stats. not linked from nav yet. --- lib/Travelynx.pm | 35 +++++++++++++ lib/Travelynx/Controller/Traveling.pm | 98 ++++++++++++++++++++++++++++++++++- templates/commute.html.ep | 89 +++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 templates/commute.html.ep diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index d5dca3f..36a3998 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -2718,6 +2718,40 @@ sub startup { } ); + $self->helper( + 'get_top_destinations' => sub { + my ( $self, %opt ) = @_; + my $uid = $opt{uid} //= $self->current_user->{id}; + my $db = $opt{db} //= $self->pg->db; + + my @stations; + + my $res = $db->query( + qq{ + select arr_eva, count(arr_eva) as count + from journeys_str + where user_id = ? + and real_dep_ts between ? and ? + group by arr_eva + order by count + limit 5 + }, $uid, $opt{after}->epoch, $opt{before}->epoch + ); + + for my $dest ( $res->hashes->each ) { + $self->app->log->debug( $dest->{arr_eva} ); + $self->app->log->debug( $dest->{count} ); + if ( my $station + = $self->app->station_by_eva->{ $dest->{arr_eva} } ) + { + push( @stations, $station ); + } + } + + return @stations; + } + ); + $self->helper( 'get_connection_targets' => sub { my ( $self, %opt ) = @_; @@ -4043,6 +4077,7 @@ sub startup { $authed_r->get('/export.json')->to('account#json_export'); $authed_r->get('/history.json')->to('traveling#json_history'); $authed_r->get('/history')->to('traveling#history'); + $authed_r->get('/history/commute')->to('traveling#commute'); $authed_r->get('/history/map')->to('traveling#map_history'); $authed_r->get('/history/:year')->to('traveling#yearly_history'); $authed_r->get('/history/:year/:month')->to('traveling#monthly_history'); diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index fb84f6b..bcc0090 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -104,7 +104,7 @@ sub user_status { $tw_data{description} = sprintf( '%s %s von %s nach %s', $status->{train_type}, $status->{train_line} // $status->{train_no}, - $status->{dep_name}, $status->{arr_name} // 'irgendwo' + $status->{dep_name}, $status->{arr_name} // 'irgendwo' ); if ( $status->{real_arrival}->epoch ) { $tw_data{description} .= $status->{real_arrival} @@ -439,7 +439,7 @@ sub station { @results = map { $_->[0] } sort { $b->[1] <=> $a->[1] } - map { [ $_, $_->departure->epoch // $_->sched_departure->epoch ] } + map { [ $_, $_->departure->epoch // $_->sched_departure->epoch ] } @results; if ($train) { @@ -488,6 +488,100 @@ sub history { $self->render( template => 'history' ); } +sub commute { + my ($self) = @_; + + my $year = $self->param('year'); + my $filter_type = $self->param('filter_type') || 'exact'; + my $station = $self->param('station'); + + # DateTime is very slow when looking far into the future due to DST changes + # -> Limit time range to avoid accidental DoS. + if ( + not( $year + and $year =~ m{ ^ [0-9]{4} $ }x + and $year > 1990 + and $year < 2100 ) + ) + { + $year = DateTime->now( time_zone => 'Europe/Berlin' )->year - 1; + } + my $interval_start = DateTime->new( + time_zone => 'Europe/Berlin', + year => $year, + month => 1, + day => 1, + hour => 0, + minute => 0, + second => 0, + ); + my $interval_end = $interval_start->clone->add( years => 1 ); + + if ( not $station ) { + my @top_station_ids = $self->get_top_destinations( + after => $interval_start, + before => $interval_end, + ); + if (@top_station_ids) { + $station = $top_station_ids[0][1]; + } + } + + my @journeys = $self->get_user_travels( + after => $interval_start, + before => $interval_end, + with_datetime => 1, + ); + + my %journeys_by_month; + my $total = 0; + + for my $journey ( reverse @journeys ) { + my $month = $journey->{rt_departure}->month; + if ( + $filter_type eq 'exact' + and ( $journey->{to_name} eq $station + or $journey->{from_name} eq $station ) + ) + { + push( @{ $journeys_by_month{$month} }, $journey ); + $total++; + } + elsif ( + $filter_type eq 'substring' + and ( $journey->{to_name} =~ m{\Q$station\E} + or $journey->{from_name} =~ m{\Q$station\E} ) + ) + { + push( @{ $journeys_by_month{$month} }, $journey ); + $total++; + } + elsif ( + $filter_type eq 'regex' + and ( $journey->{to_name} =~ m{$station} + or $journey->{from_name} =~ m{$station} ) + ) + { + push( @{ $journeys_by_month{$month} }, $journey ); + $total++; + } + } + + $self->param( year => $year ); + $self->param( filter_type => $filter_type ); + $self->param( station => $station ); + + $self->render( + template => 'commute', + with_autocomplete => 1, + journeys_by_month => \%journeys_by_month, + total_journeys => $total, + months => [ + qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember) + ], + ); +} + sub map_history { my ($self) = @_; diff --git a/templates/commute.html.ep b/templates/commute.html.ep new file mode 100644 index 0000000..ef1c7ab --- /dev/null +++ b/templates/commute.html.ep @@ -0,0 +1,89 @@ +

Pendel-Statistiken

+ +
+
+

Diese Daten können zum Beispiel für die Angaben zur Pendlerpauschale + bei der Steuererklärung genutzt werden.

+
+
+ +%= form_for '/history/commute' => begin +
+
+ %= text_field 'year', id => 'year', class => 'validate', pattern => '[0-9][0-9][0-9][0-9]' + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+ %= text_field 'station', id => 'station', required => undef, class => 'autocomplete contrast-color-text' + +
+
+
+
+ +
+
+%= end + +

<%= param('year') %>

+
+
+

+ An <%= $total_journeys %> Tagen im Jahr wurde mindestens + eine Zugfahrt von oder zu + % if (param('filter_type') eq 'exact') { + der ausgewählten Station + % } + % else { + den ausgewählten Stationen + % } + eingetragen. +

+ + + + + + + + + % for my $i (0 .. $#{$months}) { + + + + + % } + +
MonatTage mit Fahrten
<%= $months->[$i] %><%= scalar @{$journeys_by_month->{$i+1} // []} %>
+
+
+ +% for my $i (0 .. $#{$months}) { +

<%= $months->[$i] %>

+ %= include '_history_trains', date_format => '%d.%m.', journeys => $journeys_by_month->{$i+1} // [] +% } -- cgit v1.2.3