add commute stats. not linked from nav yet.
This commit is contained in:
parent
50d8256f6f
commit
94932c4102
3 changed files with 220 additions and 2 deletions
|
@ -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(
|
$self->helper(
|
||||||
'get_connection_targets' => sub {
|
'get_connection_targets' => sub {
|
||||||
my ( $self, %opt ) = @_;
|
my ( $self, %opt ) = @_;
|
||||||
|
@ -4043,6 +4077,7 @@ sub startup {
|
||||||
$authed_r->get('/export.json')->to('account#json_export');
|
$authed_r->get('/export.json')->to('account#json_export');
|
||||||
$authed_r->get('/history.json')->to('traveling#json_history');
|
$authed_r->get('/history.json')->to('traveling#json_history');
|
||||||
$authed_r->get('/history')->to('traveling#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/map')->to('traveling#map_history');
|
||||||
$authed_r->get('/history/:year')->to('traveling#yearly_history');
|
$authed_r->get('/history/:year')->to('traveling#yearly_history');
|
||||||
$authed_r->get('/history/:year/:month')->to('traveling#monthly_history');
|
$authed_r->get('/history/:year/:month')->to('traveling#monthly_history');
|
||||||
|
|
|
@ -488,6 +488,100 @@ sub history {
|
||||||
$self->render( template => '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 {
|
sub map_history {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
|
89
templates/commute.html.ep
Normal file
89
templates/commute.html.ep
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<h1>Pendel-Statistiken</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<p>Diese Daten können zum Beispiel für die Angaben zur Pendlerpauschale
|
||||||
|
bei der Steuererklärung genutzt werden.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
%= form_for '/history/commute' => begin
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-field col s12 m12 l12">
|
||||||
|
%= text_field 'year', id => 'year', class => 'validate', pattern => '[0-9][0-9][0-9][0-9]'
|
||||||
|
<label for="year">Jahr</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-field col s12 m12 l6">
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
%= radio_button filter_type => 'exact'
|
||||||
|
<span>Name der Station ist:</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
%= radio_button filter_type => 'substring'
|
||||||
|
<span>Name der Station enthält:</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
%= radio_button filter_type => 'regex'
|
||||||
|
<span>Name der Station erfüllt den regulären Ausdruck:</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-field col s12 m12 l6">
|
||||||
|
%= text_field 'station', id => 'station', required => undef, class => 'autocomplete contrast-color-text'
|
||||||
|
<label for="station">Fahrtziel</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m12 l12 center-align">
|
||||||
|
<button class="btn waves-effect waves-light" type="submit" name="action" value="show">
|
||||||
|
<i class="material-icons left" aria-hidden="true">send</i>
|
||||||
|
Anzeigen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
%= end
|
||||||
|
|
||||||
|
<h2><%= param('year') %></h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m12 l12 center-align">
|
||||||
|
<p>
|
||||||
|
An <b><%= $total_journeys %></b> 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.
|
||||||
|
</p>
|
||||||
|
<table class="striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Monat</th>
|
||||||
|
<th>Tage mit Fahrten</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
% for my $i (0 .. $#{$months}) {
|
||||||
|
<tr>
|
||||||
|
<td><%= $months->[$i] %></td>
|
||||||
|
<td><%= scalar @{$journeys_by_month->{$i+1} // []} %></td>
|
||||||
|
</tr>
|
||||||
|
% }
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
% for my $i (0 .. $#{$months}) {
|
||||||
|
<h2><%= $months->[$i] %></h2>
|
||||||
|
%= include '_history_trains', date_format => '%d.%m.', journeys => $journeys_by_month->{$i+1} // []
|
||||||
|
% }
|
Loading…
Reference in a new issue