Loading lib/Travelynx.pm +119 −15 Original line number Diff line number Diff line Loading @@ -300,6 +300,29 @@ sub startup { ); } ); $self->attr( get_interval_actions_query => sub { my ($self) = @_; # Note: Selecting on real_time would be more intuitive, but is not # possible at the moment -- non-realtime checkouts and undo actions # lack both sched_time and real_time. return $self->app->dbh->prepare( qq{ select action_id, extract(epoch from action_time), stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages from user_actions left outer join stations on station_id = stations.id where user_id = ? and action_time >= to_timestamp(?) and action_time < to_timestamp(?) order by action_time desc } ); } ); $self->attr( get_journey_actions_query => sub { my ($self) = @_; Loading Loading @@ -881,17 +904,45 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} 'get_user_travels' => sub { my ( $self, %opt ) = @_; my $uid = $self->current_user->{id}; my $uid = $opt{uid} || $self->current_user->{id}; my $query = $self->app->get_all_actions_query; if ( $opt{limit} ) { $query = $self->app->get_last_actions_query; } if ( $opt{uid} and $opt{checkin_epoch} and $opt{checkout_epoch} ) { if ( $opt{checkin_epoch} and $opt{checkout_epoch} ) { $query = $self->app->get_journey_actions_query; $query->execute( $opt{uid}, $opt{checkin_epoch}, $query->execute( $uid, $opt{checkin_epoch}, $opt{checkout_epoch} ); } elsif ( $opt{after} and $opt{before} ) { # Each journey consists of at least two database entries: one for # checkin, one for checkout. A simple query using e.g. # after = YYYY-01-01T00:00:00 and before YYYY-02-01T00:00:00 # will miss journeys where checkin and checkout take place in # different months. # We therefore add one day to the before timestamp and filter out # journeys whose checkin lies outside the originally requested # time range afterwards. # For an additional twist, get_interval_actions_query filters based # on the action time, not actual departure, as undo and force # checkout actions lack sched_time and real_time data. By # subtracting one day from "after" (i.e., moving it one day into # the past), we make sure not to miss journeys where the real departure # time falls into the interval, but the checkin time does not. # Again, this is addressed in postprocessing at the bottom of this # helper. # This works under the assumption that there are no DB trains whose # journey takes more than 24 hours. If this no longer holds, # please adjust the intervals accordingly. $query = $self->app->get_interval_actions_query; $query->execute( $uid, $opt{after}->clone->subtract( days => 1 )->epoch, $opt{before}->clone->add( days => 1 )->epoch ); } else { $query->execute($uid); } Loading Loading @@ -989,18 +1040,15 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} = $self->get_travel_distance( $ref->{from_name}, $ref->{to_name}, [ $ref->{from_name}, $ref->{to_name} ] ); $ref->{kmh_route} = $ref->{km_route} / ( ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600 ); $ref->{kmh_beeline} = $ref->{km_beeline} / ( ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600 ); my $kmh_divisor = ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600; $ref->{kmh_route} = $kmh_divisor ? $ref->{km_route} / $kmh_divisor : -1; $ref->{kmh_beeline} = $kmh_divisor ? $ref->{km_beeline} / $kmh_divisor : -1; } if ( $opt{checkin_epoch} and $action Loading @@ -1012,6 +1060,13 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $prev_action = $action; } if ( $opt{before} and $opt{after} ) { @travels = grep { $_->{rt_departure} >= $opt{after} and $_->{rt_departure} < $opt{before} } @travels; } return @travels; } ); Loading Loading @@ -1111,6 +1166,54 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'compute_journey_stats' => sub { my ( $self, @journeys ) = @_; my $km_route = 0; my $km_beeline = 0; my $min_travel_sched = 0; my $min_travel_real = 0; my $delay_dep = 0; my $delay_arr = 0; my $num_trains = 0; my $num_journeys = 0; for my $journey (@journeys) { $num_trains++; $num_journeys++; $km_route += $journey->{km_route}; $km_beeline += $journey->{km_beeline}; if ( $journey->{sched_duration} > 0 ) { $min_travel_sched += $journey->{sched_duration} / 60; } if ( $journey->{rt_duration} > 0 ) { $min_travel_real += $journey->{rt_duration} / 60; } if ( $journey->{sched_departure} and $journey->{rt_departure} ) { $delay_dep += ( $journey->{rt_departure}->epoch - $journey->{sched_departure}->epoch ) / 60; } if ( $journey->{sched_arrival} and $journey->{rt_arrival} ) { $delay_arr += ( $journey->{rt_arrival}->epoch - $journey->{sched_arrival}->epoch ) / 60; } } return { km_route => $km_route, km_beeline => $km_beeline, num_trains => $num_trains, num_journeys => $num_journeys, min_travel_sched => $min_travel_sched, min_travel_real => $min_travel_real, delay_dep => $delay_dep, delay_arr => $delay_arr, }; } ); $self->helper( 'navbar_class' => sub { my ( $self, $path ) = @_; Loading Loading @@ -1152,6 +1255,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $authed_r->get('/account')->to('account#account'); $authed_r->get('/export.json')->to('account#json_export'); $authed_r->get('/history')->to('traveling#history'); $authed_r->get('/history/:year/:month')->to('traveling#monthly_history'); $authed_r->get('/history.json')->to('traveling#json_history'); $authed_r->get('/journey/:id')->to('traveling#journey_details'); $authed_r->get('/s/*station')->to('traveling#station'); Loading lib/Travelynx/Controller/Traveling.pm +62 −3 Original line number Diff line number Diff line package Travelynx::Controller::Traveling; use Mojo::Base 'Mojolicious::Controller'; use DateTime; use Travel::Status::DE::IRIS::Stations; sub homepage { Loading Loading @@ -233,10 +234,14 @@ sub history { my ($self) = @_; my $cancelled = $self->param('cancelled') ? 1 : 0; my @journeys = $self->get_user_travels( cancelled => $cancelled ); $self->respond_to( json => { json => [ $self->get_user_travels( cancelled => $cancelled ) ] }, any => { template => 'history' } json => { json => [@journeys] }, any => { template => 'history', journeys => [@journeys] } ); } Loading @@ -248,6 +253,60 @@ sub json_history { json => [ $self->get_user_travels( cancelled => $cancelled ) ] ); } sub monthly_history { my ($self) = @_; my $year = $self->stash('year'); my $month = $self->stash('month'); my $cancelled = $self->param('cancelled') ? 1 : 0; my @journeys; my $stats; my @months = ( qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember) ); if ( not( $year =~ m{ ^ [0-9]{4} $ }x and $month =~ m{ ^ [0-9]{1,2} $ }x ) ) { @journeys = $self->get_user_travels( cancelled => $cancelled ); } else { my $interval_start = DateTime->new( time_zone => 'Europe/Berlin', year => $year, month => $month, day => 1, hour => 0, minute => 0, second => 0, ); my $interval_end = $interval_start->clone->add( months => 1 ); @journeys = $self->get_user_travels( cancelled => $cancelled, verbose => 1, after => $interval_start, before => $interval_end ); $stats = $self->compute_journey_stats(@journeys); } $self->respond_to( json => { json => { journeys => [@journeys], statistics => $stats } }, any => { template => 'history', journeys => [@journeys], year => $year, month => $months[ $month - 1 ], statistics => $stats } ); } sub journey_details { my ($self) = @_; my ( $uid, $checkin_ts, $checkout_ts ) = split( qr{-}, $self->stash('id') ); Loading templates/history.html.ep +31 −3 Original line number Diff line number Diff line % if (param('cancelled')) { % if (my $stats = stash('statistics')) { <h1><%= stash('month') %> <%= stash('year') %></h1> <div class="row"> <div class="col s12"> <table class="striped"> <tr> <th scope="row">Fahrten</th> <td><%= $stats->{num_trains} %></td> </tr> <tr> <th scope="row">Entfernung</th> <td>ca. <%= sprintf('%.f', $stats->{km_route}) %> km (Luftlinie: <%= sprintf('%.f', $stats->{km_beeline}) %> km)</td> </tr> <tr> <th scope="row">Fahrtzeit</th> <td><%= sprintf('%02d:%02d', $stats->{min_travel_real} / 60, $stats->{min_travel_real} % 60) %> Stunden (nach Fahrplan: <%= sprintf('%02d:%02d', $stats->{min_travel_sched} / 60, $stats->{min_travel_sched} % 60) %>)<td> </tr> <tr> <th scope="row">Kumulierte Verspätung</th> <td>Bei Abfahrt: <%= sprintf('%02d:%02d', $stats->{delay_dep} / 60, $stats->{delay_dep} % 60) %> Stunden<br/> Bei Ankunft: <%= sprintf('%02d:%02d', $stats->{delay_arr} / 60, $stats->{delay_arr} % 60) %> Stunden</td> </tr> </table> </div> </div> % } % elsif (param('cancelled')) { <h1>Ausgefallene Fahrten</h1> <div class="row"> <div class="col s12"> Loading @@ -10,7 +38,7 @@ </div> % } % else { <h1>Bisherige Fahrten</h1> <h1>Fahrten</h1> <div class="row"> <div class="col s12"> <ul> Loading @@ -33,7 +61,7 @@ </tr> </thead> <tbody> % for my $travel (get_user_travels(cancelled => (param('cancelled') ? 1 : 0))) { % for my $travel (@{$journeys}) { % if ($travel->{completed}) { % my $detail_link = '/journey/' . current_user()->{id} . '-' . $travel->{checkin}->epoch . '-' . $travel->{checkout}->epoch; <tr> Loading Loading
lib/Travelynx.pm +119 −15 Original line number Diff line number Diff line Loading @@ -300,6 +300,29 @@ sub startup { ); } ); $self->attr( get_interval_actions_query => sub { my ($self) = @_; # Note: Selecting on real_time would be more intuitive, but is not # possible at the moment -- non-realtime checkouts and undo actions # lack both sched_time and real_time. return $self->app->dbh->prepare( qq{ select action_id, extract(epoch from action_time), stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages from user_actions left outer join stations on station_id = stations.id where user_id = ? and action_time >= to_timestamp(?) and action_time < to_timestamp(?) order by action_time desc } ); } ); $self->attr( get_journey_actions_query => sub { my ($self) = @_; Loading Loading @@ -881,17 +904,45 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} 'get_user_travels' => sub { my ( $self, %opt ) = @_; my $uid = $self->current_user->{id}; my $uid = $opt{uid} || $self->current_user->{id}; my $query = $self->app->get_all_actions_query; if ( $opt{limit} ) { $query = $self->app->get_last_actions_query; } if ( $opt{uid} and $opt{checkin_epoch} and $opt{checkout_epoch} ) { if ( $opt{checkin_epoch} and $opt{checkout_epoch} ) { $query = $self->app->get_journey_actions_query; $query->execute( $opt{uid}, $opt{checkin_epoch}, $query->execute( $uid, $opt{checkin_epoch}, $opt{checkout_epoch} ); } elsif ( $opt{after} and $opt{before} ) { # Each journey consists of at least two database entries: one for # checkin, one for checkout. A simple query using e.g. # after = YYYY-01-01T00:00:00 and before YYYY-02-01T00:00:00 # will miss journeys where checkin and checkout take place in # different months. # We therefore add one day to the before timestamp and filter out # journeys whose checkin lies outside the originally requested # time range afterwards. # For an additional twist, get_interval_actions_query filters based # on the action time, not actual departure, as undo and force # checkout actions lack sched_time and real_time data. By # subtracting one day from "after" (i.e., moving it one day into # the past), we make sure not to miss journeys where the real departure # time falls into the interval, but the checkin time does not. # Again, this is addressed in postprocessing at the bottom of this # helper. # This works under the assumption that there are no DB trains whose # journey takes more than 24 hours. If this no longer holds, # please adjust the intervals accordingly. $query = $self->app->get_interval_actions_query; $query->execute( $uid, $opt{after}->clone->subtract( days => 1 )->epoch, $opt{before}->clone->add( days => 1 )->epoch ); } else { $query->execute($uid); } Loading Loading @@ -989,18 +1040,15 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} = $self->get_travel_distance( $ref->{from_name}, $ref->{to_name}, [ $ref->{from_name}, $ref->{to_name} ] ); $ref->{kmh_route} = $ref->{km_route} / ( ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600 ); $ref->{kmh_beeline} = $ref->{km_beeline} / ( ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600 ); my $kmh_divisor = ( $ref->{rt_duration} // $ref->{sched_duration} // 999999 ) / 3600; $ref->{kmh_route} = $kmh_divisor ? $ref->{km_route} / $kmh_divisor : -1; $ref->{kmh_beeline} = $kmh_divisor ? $ref->{km_beeline} / $kmh_divisor : -1; } if ( $opt{checkin_epoch} and $action Loading @@ -1012,6 +1060,13 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $prev_action = $action; } if ( $opt{before} and $opt{after} ) { @travels = grep { $_->{rt_departure} >= $opt{after} and $_->{rt_departure} < $opt{before} } @travels; } return @travels; } ); Loading Loading @@ -1111,6 +1166,54 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'compute_journey_stats' => sub { my ( $self, @journeys ) = @_; my $km_route = 0; my $km_beeline = 0; my $min_travel_sched = 0; my $min_travel_real = 0; my $delay_dep = 0; my $delay_arr = 0; my $num_trains = 0; my $num_journeys = 0; for my $journey (@journeys) { $num_trains++; $num_journeys++; $km_route += $journey->{km_route}; $km_beeline += $journey->{km_beeline}; if ( $journey->{sched_duration} > 0 ) { $min_travel_sched += $journey->{sched_duration} / 60; } if ( $journey->{rt_duration} > 0 ) { $min_travel_real += $journey->{rt_duration} / 60; } if ( $journey->{sched_departure} and $journey->{rt_departure} ) { $delay_dep += ( $journey->{rt_departure}->epoch - $journey->{sched_departure}->epoch ) / 60; } if ( $journey->{sched_arrival} and $journey->{rt_arrival} ) { $delay_arr += ( $journey->{rt_arrival}->epoch - $journey->{sched_arrival}->epoch ) / 60; } } return { km_route => $km_route, km_beeline => $km_beeline, num_trains => $num_trains, num_journeys => $num_journeys, min_travel_sched => $min_travel_sched, min_travel_real => $min_travel_real, delay_dep => $delay_dep, delay_arr => $delay_arr, }; } ); $self->helper( 'navbar_class' => sub { my ( $self, $path ) = @_; Loading Loading @@ -1152,6 +1255,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $authed_r->get('/account')->to('account#account'); $authed_r->get('/export.json')->to('account#json_export'); $authed_r->get('/history')->to('traveling#history'); $authed_r->get('/history/:year/:month')->to('traveling#monthly_history'); $authed_r->get('/history.json')->to('traveling#json_history'); $authed_r->get('/journey/:id')->to('traveling#journey_details'); $authed_r->get('/s/*station')->to('traveling#station'); Loading
lib/Travelynx/Controller/Traveling.pm +62 −3 Original line number Diff line number Diff line package Travelynx::Controller::Traveling; use Mojo::Base 'Mojolicious::Controller'; use DateTime; use Travel::Status::DE::IRIS::Stations; sub homepage { Loading Loading @@ -233,10 +234,14 @@ sub history { my ($self) = @_; my $cancelled = $self->param('cancelled') ? 1 : 0; my @journeys = $self->get_user_travels( cancelled => $cancelled ); $self->respond_to( json => { json => [ $self->get_user_travels( cancelled => $cancelled ) ] }, any => { template => 'history' } json => { json => [@journeys] }, any => { template => 'history', journeys => [@journeys] } ); } Loading @@ -248,6 +253,60 @@ sub json_history { json => [ $self->get_user_travels( cancelled => $cancelled ) ] ); } sub monthly_history { my ($self) = @_; my $year = $self->stash('year'); my $month = $self->stash('month'); my $cancelled = $self->param('cancelled') ? 1 : 0; my @journeys; my $stats; my @months = ( qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember) ); if ( not( $year =~ m{ ^ [0-9]{4} $ }x and $month =~ m{ ^ [0-9]{1,2} $ }x ) ) { @journeys = $self->get_user_travels( cancelled => $cancelled ); } else { my $interval_start = DateTime->new( time_zone => 'Europe/Berlin', year => $year, month => $month, day => 1, hour => 0, minute => 0, second => 0, ); my $interval_end = $interval_start->clone->add( months => 1 ); @journeys = $self->get_user_travels( cancelled => $cancelled, verbose => 1, after => $interval_start, before => $interval_end ); $stats = $self->compute_journey_stats(@journeys); } $self->respond_to( json => { json => { journeys => [@journeys], statistics => $stats } }, any => { template => 'history', journeys => [@journeys], year => $year, month => $months[ $month - 1 ], statistics => $stats } ); } sub journey_details { my ($self) = @_; my ( $uid, $checkin_ts, $checkout_ts ) = split( qr{-}, $self->stash('id') ); Loading
templates/history.html.ep +31 −3 Original line number Diff line number Diff line % if (param('cancelled')) { % if (my $stats = stash('statistics')) { <h1><%= stash('month') %> <%= stash('year') %></h1> <div class="row"> <div class="col s12"> <table class="striped"> <tr> <th scope="row">Fahrten</th> <td><%= $stats->{num_trains} %></td> </tr> <tr> <th scope="row">Entfernung</th> <td>ca. <%= sprintf('%.f', $stats->{km_route}) %> km (Luftlinie: <%= sprintf('%.f', $stats->{km_beeline}) %> km)</td> </tr> <tr> <th scope="row">Fahrtzeit</th> <td><%= sprintf('%02d:%02d', $stats->{min_travel_real} / 60, $stats->{min_travel_real} % 60) %> Stunden (nach Fahrplan: <%= sprintf('%02d:%02d', $stats->{min_travel_sched} / 60, $stats->{min_travel_sched} % 60) %>)<td> </tr> <tr> <th scope="row">Kumulierte Verspätung</th> <td>Bei Abfahrt: <%= sprintf('%02d:%02d', $stats->{delay_dep} / 60, $stats->{delay_dep} % 60) %> Stunden<br/> Bei Ankunft: <%= sprintf('%02d:%02d', $stats->{delay_arr} / 60, $stats->{delay_arr} % 60) %> Stunden</td> </tr> </table> </div> </div> % } % elsif (param('cancelled')) { <h1>Ausgefallene Fahrten</h1> <div class="row"> <div class="col s12"> Loading @@ -10,7 +38,7 @@ </div> % } % else { <h1>Bisherige Fahrten</h1> <h1>Fahrten</h1> <div class="row"> <div class="col s12"> <ul> Loading @@ -33,7 +61,7 @@ </tr> </thead> <tbody> % for my $travel (get_user_travels(cancelled => (param('cancelled') ? 1 : 0))) { % for my $travel (@{$journeys}) { % if ($travel->{completed}) { % my $detail_link = '/journey/' . current_user()->{id} . '-' . $travel->{checkin}->epoch . '-' . $travel->{checkout}->epoch; <tr> Loading