2019-03-21 21:12:45 +00:00
|
|
|
|
package Travelynx::Controller::Traveling;
|
2020-11-28 20:03:51 +00:00
|
|
|
|
|
2023-02-19 08:35:38 +00:00
|
|
|
|
# Copyright (C) 2020-2023 Daniel Friesel
|
2020-11-27 21:12:56 +00:00
|
|
|
|
#
|
2021-01-29 17:32:13 +00:00
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
2019-03-21 21:12:45 +00:00
|
|
|
|
use Mojo::Base 'Mojolicious::Controller';
|
|
|
|
|
|
2019-03-27 20:20:59 +00:00
|
|
|
|
use DateTime;
|
2019-04-08 19:20:04 +00:00
|
|
|
|
use DateTime::Format::Strptime;
|
2020-02-07 22:19:01 +00:00
|
|
|
|
use JSON;
|
2022-09-03 14:46:48 +00:00
|
|
|
|
use List::Util qw(uniq min max);
|
|
|
|
|
use List::UtilsBy qw(max_by uniq_by);
|
2019-11-16 20:24:35 +00:00
|
|
|
|
use List::MoreUtils qw(first_index);
|
2022-08-01 08:07:24 +00:00
|
|
|
|
use Mojo::Promise;
|
2020-04-19 16:26:20 +00:00
|
|
|
|
use Text::CSV;
|
2019-03-21 21:12:45 +00:00
|
|
|
|
use Travel::Status::DE::IRIS::Stations;
|
|
|
|
|
|
2022-07-30 08:47:18 +00:00
|
|
|
|
# Internal Helpers
|
|
|
|
|
|
|
|
|
|
sub has_str_in_list {
|
|
|
|
|
my ( $str, @strs ) = @_;
|
|
|
|
|
if ( List::Util::any { $str eq $_ } @strs ) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
sub get_connecting_trains_p {
|
2022-07-30 08:47:18 +00:00
|
|
|
|
my ( $self, %opt ) = @_;
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
my $uid = $opt{uid} //= $self->current_user->{id};
|
|
|
|
|
my ( $use_history, $lt_stops ) = $self->users->use_history(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
with_local_transit => 1
|
|
|
|
|
);
|
2022-07-30 08:47:18 +00:00
|
|
|
|
|
|
|
|
|
my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
|
|
|
|
|
my $now = $self->now->epoch;
|
2022-09-03 14:46:48 +00:00
|
|
|
|
my ( $stationinfo, $arr_epoch, $arr_platform, $arr_countdown );
|
2022-07-30 08:47:18 +00:00
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
my $promise = Mojo::Promise->new;
|
|
|
|
|
|
2022-07-30 08:47:18 +00:00
|
|
|
|
if ( $opt{eva} ) {
|
|
|
|
|
if ( $use_history & 0x01 ) {
|
|
|
|
|
$eva = $opt{eva};
|
|
|
|
|
}
|
|
|
|
|
elsif ( $opt{destination_name} ) {
|
|
|
|
|
$eva = $opt{eva};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if ( $use_history & 0x02 ) {
|
|
|
|
|
my $status = $self->get_user_status;
|
|
|
|
|
$eva = $status->{arr_eva};
|
|
|
|
|
$exclude_via = $status->{dep_name};
|
|
|
|
|
$exclude_train_id = $status->{train_id};
|
|
|
|
|
$arr_platform = $status->{arr_platform};
|
|
|
|
|
$stationinfo = $status->{extra_data}{stationinfo_arr};
|
|
|
|
|
if ( $status->{real_arrival} ) {
|
|
|
|
|
$exclude_before = $arr_epoch = $status->{real_arrival}->epoch;
|
2022-09-03 14:46:48 +00:00
|
|
|
|
$arr_countdown = $status->{arrival_countdown};
|
2022-07-30 08:47:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$exclude_before //= $now - 300;
|
|
|
|
|
|
|
|
|
|
if ( not $eva ) {
|
2022-08-01 08:07:24 +00:00
|
|
|
|
return $promise->reject;
|
2022-07-30 08:47:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my @destinations = $self->journeys->get_connection_targets(%opt);
|
|
|
|
|
|
|
|
|
|
if ($exclude_via) {
|
|
|
|
|
@destinations = grep { $_ ne $exclude_via } @destinations;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( not( @destinations or $use_history & 0x04 and @{$lt_stops} ) ) {
|
2022-08-01 08:07:24 +00:00
|
|
|
|
return $promise->reject;
|
2022-07-30 08:47:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-03 14:58:54 +00:00
|
|
|
|
my $can_check_in = not $arr_epoch || ( $arr_countdown // 1 ) < 0;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my $lookahead
|
|
|
|
|
= $can_check_in ? 40 : ( ( ${arr_countdown} // 0 ) / 60 + 40 );
|
|
|
|
|
|
|
|
|
|
my $iris_promise = Mojo::Promise->new;
|
2022-09-03 14:46:48 +00:00
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if (@destinations) {
|
|
|
|
|
$self->iris->get_departures_p(
|
|
|
|
|
station => $eva,
|
|
|
|
|
lookbehind => 10,
|
|
|
|
|
lookahead => $lookahead,
|
|
|
|
|
with_related => 1
|
|
|
|
|
)->then(
|
|
|
|
|
sub {
|
|
|
|
|
my ($stationboard) = @_;
|
|
|
|
|
if ( $stationboard->{errstr} ) {
|
|
|
|
|
$iris_promise->reject( $stationboard->{errstr} );
|
|
|
|
|
return;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
@{ $stationboard->{results} } = map { $_->[0] }
|
|
|
|
|
sort { $a->[1] <=> $b->[1] }
|
|
|
|
|
map { [ $_, $_->departure ? $_->departure->epoch : 0 ] }
|
|
|
|
|
@{ $stationboard->{results} };
|
|
|
|
|
my @results;
|
|
|
|
|
my @cancellations;
|
|
|
|
|
my $excluded_train;
|
|
|
|
|
my %via_count = map { $_ => 0 } @destinations;
|
|
|
|
|
for my $train ( @{ $stationboard->{results} } ) {
|
|
|
|
|
if ( not $train->departure ) {
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
if ( $exclude_before
|
|
|
|
|
and $train->departure
|
|
|
|
|
and $train->departure->epoch < $exclude_before )
|
|
|
|
|
{
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
if ( $exclude_train_id
|
|
|
|
|
and $train->train_id eq $exclude_train_id )
|
|
|
|
|
{
|
|
|
|
|
$excluded_train = $train;
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
# In general, this function is meant to return feasible
|
|
|
|
|
# connections. However, cancelled connections may also be of
|
|
|
|
|
# interest and are also useful for logging cancellations.
|
|
|
|
|
# To satisfy both demands with (hopefully) little confusion and
|
|
|
|
|
# UI clutter, this function returns two concatenated arrays:
|
|
|
|
|
# actual connections (ordered by actual departure time) followed
|
|
|
|
|
# by cancelled connections (ordered by scheduled departure time).
|
|
|
|
|
# This is easiest to achieve in two separate loops.
|
|
|
|
|
#
|
|
|
|
|
# Note that a cancelled train may still have a matching destination
|
|
|
|
|
# in its route_post, e.g. if it leaves out $eva due to
|
|
|
|
|
# unscheduled route changes but continues on schedule afterwards
|
|
|
|
|
# -- so it is only cancelled at $eva, not on the remainder of
|
|
|
|
|
# the route. Also note that this specific case is not yet handled
|
|
|
|
|
# properly by the cancellation logic etc.
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( $train->departure_is_cancelled ) {
|
|
|
|
|
my @via = (
|
|
|
|
|
$train->sched_route_post, $train->sched_route_end
|
|
|
|
|
);
|
|
|
|
|
for my $dest (@destinations) {
|
|
|
|
|
if ( has_str_in_list( $dest, @via ) ) {
|
|
|
|
|
push( @cancellations, [ $train, $dest ] );
|
|
|
|
|
next;
|
|
|
|
|
}
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
else {
|
|
|
|
|
my @via = ( $train->route_post, $train->route_end );
|
|
|
|
|
for my $dest (@destinations) {
|
|
|
|
|
if ( $via_count{$dest} < 2
|
|
|
|
|
and has_str_in_list( $dest, @via ) )
|
|
|
|
|
{
|
|
|
|
|
push( @results, [ $train, $dest ] );
|
2022-07-30 08:47:18 +00:00
|
|
|
|
|
|
|
|
|
# Show all past and up to two future departures per destination
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( not $train->departure
|
|
|
|
|
or $train->departure->epoch >= $now )
|
|
|
|
|
{
|
|
|
|
|
$via_count{$dest}++;
|
|
|
|
|
}
|
|
|
|
|
next;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-30 08:47:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
@results = map { $_->[0] }
|
|
|
|
|
sort { $a->[1] <=> $b->[1] }
|
|
|
|
|
map {
|
|
|
|
|
[
|
|
|
|
|
$_,
|
|
|
|
|
$_->[0]->departure->epoch
|
|
|
|
|
// $_->[0]->sched_departure->epoch
|
|
|
|
|
]
|
|
|
|
|
} @results;
|
|
|
|
|
@cancellations = map { $_->[0] }
|
|
|
|
|
sort { $a->[1] <=> $b->[1] }
|
|
|
|
|
map { [ $_, $_->[0]->sched_departure->epoch ] }
|
|
|
|
|
@cancellations;
|
|
|
|
|
|
|
|
|
|
# remove trains whose route matches the excluded one's
|
|
|
|
|
if ($excluded_train) {
|
|
|
|
|
my $route_pre
|
|
|
|
|
= join( '|', reverse $excluded_train->route_pre );
|
|
|
|
|
@results
|
|
|
|
|
= grep { join( '|', $_->[0]->route_post ) ne $route_pre }
|
|
|
|
|
@results;
|
|
|
|
|
my $route_post = join( '|', $excluded_train->route_post );
|
|
|
|
|
@results
|
|
|
|
|
= grep { join( '|', $_->[0]->route_post ) ne $route_post }
|
|
|
|
|
@results;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
|
|
|
|
|
# add message IDs and 'transfer short' hints
|
|
|
|
|
for my $result (@results) {
|
|
|
|
|
my $train = $result->[0];
|
|
|
|
|
my @message_ids
|
|
|
|
|
= List::Util::uniq map { $_->[1] } $train->raw_messages;
|
|
|
|
|
$train->{message_id} = { map { $_ => 1 } @message_ids };
|
|
|
|
|
my $interchange_duration;
|
|
|
|
|
if ( exists $stationinfo->{i} ) {
|
2022-12-26 19:47:43 +00:00
|
|
|
|
if ( defined $arr_platform
|
|
|
|
|
and defined $train->platform )
|
|
|
|
|
{
|
|
|
|
|
$interchange_duration
|
|
|
|
|
= $stationinfo->{i}{$arr_platform}
|
|
|
|
|
{ $train->platform };
|
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
$interchange_duration //= $stationinfo->{i}{"*"};
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( defined $interchange_duration ) {
|
|
|
|
|
my $interchange_time
|
|
|
|
|
= ( $train->departure->epoch - $arr_epoch ) / 60;
|
|
|
|
|
if ( $interchange_time < $interchange_duration ) {
|
|
|
|
|
$train->{interchange_text} = 'Anschluss knapp';
|
|
|
|
|
$train->{interchange_icon} = 'directions_run';
|
|
|
|
|
}
|
|
|
|
|
elsif ( $interchange_time == $interchange_duration ) {
|
|
|
|
|
$train->{interchange_text}
|
|
|
|
|
= 'Anschluss könnte knapp werden';
|
|
|
|
|
$train->{interchange_icon} = 'directions_run';
|
|
|
|
|
}
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
2022-09-24 14:11:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
$iris_promise->resolve( [ @results, @cancellations ] );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
$iris_promise->reject(@_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$iris_promise->resolve( [] );
|
|
|
|
|
}
|
2022-07-30 08:47:18 +00:00
|
|
|
|
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my $hafas_promise = Mojo::Promise->new;
|
2022-11-09 17:14:07 +00:00
|
|
|
|
$self->hafas->get_departures_p(
|
|
|
|
|
eva => $eva,
|
|
|
|
|
lookbehind => 10,
|
|
|
|
|
lookahead => $lookahead
|
2022-09-24 14:11:51 +00:00
|
|
|
|
)->then(
|
|
|
|
|
sub {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
my ($status) = @_;
|
|
|
|
|
$hafas_promise->resolve( [ $status->results ] );
|
2022-09-24 14:11:51 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
# HAFAS data is optional.
|
|
|
|
|
# Errors are logged by get_json_p and can be silently ignored here.
|
|
|
|
|
$hafas_promise->resolve( [] );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
|
|
|
|
|
Mojo::Promise->all( $iris_promise, $hafas_promise )->then(
|
|
|
|
|
sub {
|
|
|
|
|
my ( $iris, $hafas ) = @_;
|
|
|
|
|
my @iris_trains = @{ $iris->[0] };
|
|
|
|
|
my @hafas_trains = @{ $hafas->[0] };
|
2022-09-24 16:53:04 +00:00
|
|
|
|
my @transit_fyi;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
|
|
|
|
|
# We've already got a list of connecting trains; this function
|
|
|
|
|
# only adds further information to them. We ignore errors, as
|
|
|
|
|
# partial data is better than no data.
|
|
|
|
|
eval {
|
|
|
|
|
for my $iris_train (@iris_trains) {
|
|
|
|
|
if ( $iris_train->[0]->departure_is_cancelled ) {
|
2022-09-28 17:37:20 +00:00
|
|
|
|
next;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
}
|
|
|
|
|
for my $hafas_train (@hafas_trains) {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
if ( $hafas_train->number
|
|
|
|
|
and $hafas_train->number
|
2022-09-24 14:11:51 +00:00
|
|
|
|
== $iris_train->[0]->train_no )
|
|
|
|
|
{
|
2022-11-12 17:37:00 +00:00
|
|
|
|
if ( $hafas_train->load
|
|
|
|
|
and $hafas_train->load->{SECOND} )
|
|
|
|
|
{
|
|
|
|
|
$iris_train->[3] = $hafas_train->load;
|
|
|
|
|
}
|
2022-11-09 17:14:07 +00:00
|
|
|
|
for my $stop ( $hafas_train->route ) {
|
|
|
|
|
if ( $stop->{name}
|
|
|
|
|
and $stop->{name} eq $iris_train->[1]
|
|
|
|
|
and $stop->{arr} )
|
2022-09-24 14:11:51 +00:00
|
|
|
|
{
|
2022-11-09 17:14:07 +00:00
|
|
|
|
$iris_train->[2] = $stop->{arr};
|
|
|
|
|
if ( $iris_train->[0]->departure_delay
|
|
|
|
|
and not $stop->{arr_delay} )
|
2022-09-24 14:11:51 +00:00
|
|
|
|
{
|
2022-09-28 17:40:31 +00:00
|
|
|
|
$iris_train->[2]
|
|
|
|
|
->add( minutes => $iris_train->[0]
|
|
|
|
|
->departure_delay );
|
2022-09-24 14:11:51 +00:00
|
|
|
|
}
|
2022-09-28 17:37:20 +00:00
|
|
|
|
last;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-28 17:37:20 +00:00
|
|
|
|
last;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( $use_history & 0x04 and @{$lt_stops} ) {
|
|
|
|
|
my %via_count = map { $_ => 0 } @{$lt_stops};
|
|
|
|
|
for my $hafas_train (@hafas_trains) {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
for my $stop ( $hafas_train->route ) {
|
2022-09-24 16:53:04 +00:00
|
|
|
|
for my $dest ( @{$lt_stops} ) {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
if ( $stop->{name}
|
|
|
|
|
and $stop->{name} eq $dest
|
2022-09-24 16:53:04 +00:00
|
|
|
|
and $via_count{$dest} < 2
|
2022-11-09 17:14:07 +00:00
|
|
|
|
and $hafas_train->datetime )
|
2022-09-24 16:53:04 +00:00
|
|
|
|
{
|
2022-11-09 17:14:07 +00:00
|
|
|
|
my $departure = $hafas_train->datetime;
|
|
|
|
|
my $arrival = $stop->{arr};
|
|
|
|
|
my $delay = $hafas_train->delay;
|
|
|
|
|
if ( $delay
|
|
|
|
|
and $stop->{arr} == $stop->{sched_arr} )
|
|
|
|
|
{
|
|
|
|
|
$arrival->add( minutes => $delay );
|
2022-09-25 14:54:06 +00:00
|
|
|
|
}
|
2022-09-24 16:53:04 +00:00
|
|
|
|
if ( $departure->epoch >= $exclude_before )
|
|
|
|
|
{
|
|
|
|
|
$via_count{$dest}++;
|
|
|
|
|
push(
|
|
|
|
|
@transit_fyi,
|
|
|
|
|
[
|
|
|
|
|
{
|
2022-11-09 17:14:07 +00:00
|
|
|
|
line => $hafas_train->line,
|
2022-09-24 16:53:04 +00:00
|
|
|
|
departure => $departure,
|
2022-09-25 14:54:06 +00:00
|
|
|
|
departure_delay => $delay
|
2022-09-24 16:53:04 +00:00
|
|
|
|
},
|
|
|
|
|
$dest, $arrival
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-24 14:11:51 +00:00
|
|
|
|
};
|
|
|
|
|
if ($@) {
|
|
|
|
|
$self->app->log->error(
|
|
|
|
|
"get_connecting_trains_p($uid): IRIS/HAFAS merge failed: $@"
|
|
|
|
|
);
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
2022-07-30 08:47:18 +00:00
|
|
|
|
|
2022-09-24 16:53:04 +00:00
|
|
|
|
$promise->resolve( \@iris_trains, \@transit_fyi );
|
2022-09-24 14:11:51 +00:00
|
|
|
|
return;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ($err) = @_;
|
|
|
|
|
|
|
|
|
|
# TODO logging. HAFAS errors should never happen, IRIS errors are noteworthy too.
|
|
|
|
|
$promise->reject($err);
|
2022-08-01 08:07:24 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
2022-09-24 14:11:51 +00:00
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
return $promise;
|
2022-07-30 08:47:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
sub compute_effective_visibility {
|
|
|
|
|
my ( $self, $default_visibility, $journey_visibility ) = @_;
|
|
|
|
|
if ( $journey_visibility eq 'default' ) {
|
|
|
|
|
return $default_visibility;
|
|
|
|
|
}
|
|
|
|
|
return $journey_visibility;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-30 08:47:18 +00:00
|
|
|
|
# Controllers
|
|
|
|
|
|
2019-03-21 21:12:45 +00:00
|
|
|
|
sub homepage {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
if ( $self->is_user_authenticated ) {
|
2022-07-27 11:54:20 +00:00
|
|
|
|
my $status = $self->get_user_status;
|
2023-01-18 17:13:35 +00:00
|
|
|
|
my @recent_targets;
|
2022-07-27 11:54:20 +00:00
|
|
|
|
if ( $status->{checked_in} ) {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $journey_visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$self->current_user->{default_visibility_str},
|
|
|
|
|
$status->{visibility_str} );
|
2022-07-27 11:54:20 +00:00
|
|
|
|
if ( defined $status->{arrival_countdown}
|
2022-08-26 16:38:54 +00:00
|
|
|
|
and $status->{arrival_countdown} < ( 40 * 60 ) )
|
2022-07-27 11:54:20 +00:00
|
|
|
|
{
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render_later;
|
|
|
|
|
$self->get_connecting_trains_p->then(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ( $connecting_trains, $transit_fyi ) = @_;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
|
|
|
|
version => $self->app->config->{version}
|
|
|
|
|
// 'UNKNOWN',
|
2023-02-27 21:14:54 +00:00
|
|
|
|
user_status => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
|
|
|
|
connections => $connecting_trains,
|
|
|
|
|
transit_fyi => $transit_fyi,
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
$self->users->mark_seen(
|
|
|
|
|
uid => $self->current_user->{id} );
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
|
|
|
|
version => $self->app->config->{version}
|
|
|
|
|
// 'UNKNOWN',
|
2023-02-27 21:14:54 +00:00
|
|
|
|
user_status => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
$self->users->mark_seen(
|
|
|
|
|
uid => $self->current_user->{id} );
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
return;
|
2022-07-27 11:54:20 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
|
|
|
|
user_status => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
|
|
|
|
);
|
|
|
|
|
$self->users->mark_seen( uid => $self->current_user->{id} );
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-27 11:54:20 +00:00
|
|
|
|
}
|
2023-01-18 17:13:35 +00:00
|
|
|
|
else {
|
2023-01-22 09:32:06 +00:00
|
|
|
|
@recent_targets = uniq_by { $_->{eva} }
|
|
|
|
|
$self->journeys->get_latest_checkout_stations(
|
2023-01-18 17:13:35 +00:00
|
|
|
|
uid => $self->current_user->{id} );
|
|
|
|
|
}
|
2019-04-13 08:43:05 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
2019-04-30 16:18:09 +00:00
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
2022-08-04 12:07:25 +00:00
|
|
|
|
user_status => $status,
|
2023-01-18 17:13:35 +00:00
|
|
|
|
recent_targets => \@recent_targets,
|
2019-04-13 08:43:05 +00:00
|
|
|
|
with_autocomplete => 1,
|
|
|
|
|
with_geolocation => 1
|
|
|
|
|
);
|
2020-07-27 16:53:22 +00:00
|
|
|
|
$self->users->mark_seen( uid => $self->current_user->{id} );
|
2019-03-21 21:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2019-04-30 16:18:09 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
|
|
|
|
intro => 1
|
|
|
|
|
);
|
2019-03-21 21:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
sub status_token_ok {
|
|
|
|
|
my ( $self, $status, $ts2_ext ) = @_;
|
|
|
|
|
my $token = $self->param('token') // q{};
|
|
|
|
|
|
|
|
|
|
my ( $eva, $ts, $ts2 ) = split( qr{-}, $token );
|
|
|
|
|
if ( not $ts ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ts2 //= $ts2_ext;
|
|
|
|
|
|
|
|
|
|
if ( $eva == $status->{dep_eva}
|
2023-03-03 14:05:43 +00:00
|
|
|
|
and $ts == $status->{timestamp}->epoch % 337
|
2023-02-27 21:14:54 +00:00
|
|
|
|
and $ts2 == $status->{sched_departure}->epoch )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-28 18:45:27 +00:00
|
|
|
|
sub journey_token_ok {
|
|
|
|
|
my ( $self, $journey, $ts2_ext ) = @_;
|
|
|
|
|
my $token = $self->param('token') // q{};
|
|
|
|
|
|
|
|
|
|
my ( $eva, $ts, $ts2 ) = split( qr{-}, $token );
|
|
|
|
|
if ( not $ts ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ts2 //= $ts2_ext;
|
|
|
|
|
|
|
|
|
|
if ( $eva == $journey->{from_eva}
|
2023-03-03 14:05:43 +00:00
|
|
|
|
and $ts == $journey->{checkin_ts} % 337
|
2023-02-28 18:45:27 +00:00
|
|
|
|
and $ts2 == $journey->{sched_dep_ts} )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-30 21:23:49 +00:00
|
|
|
|
sub user_status {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
my $name = $self->stash('name');
|
2020-02-19 19:58:38 +00:00
|
|
|
|
my $ts = $self->stash('ts') // 0;
|
2020-07-27 16:53:22 +00:00
|
|
|
|
my $user = $self->users->get_privacy_by_name( name => $name );
|
2019-04-30 21:23:49 +00:00
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
if ( not $user ) {
|
2020-02-19 19:58:38 +00:00
|
|
|
|
$self->render('not_found');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $status = $self->get_user_status( $user->{id} );
|
|
|
|
|
|
2019-12-08 10:06:17 +00:00
|
|
|
|
if (
|
2020-02-19 19:58:38 +00:00
|
|
|
|
$ts
|
|
|
|
|
and ( not $status->{checked_in}
|
|
|
|
|
or $status->{sched_departure}->epoch != $ts )
|
2019-12-08 10:06:17 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
2023-03-02 16:54:09 +00:00
|
|
|
|
for my $journey (
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$self->journeys->get(
|
2023-03-02 16:54:09 +00:00
|
|
|
|
uid => $user->{id},
|
|
|
|
|
sched_dep_ts => $ts,
|
|
|
|
|
limit => 1,
|
|
|
|
|
with_visibility => 1,
|
2020-02-19 19:58:38 +00:00
|
|
|
|
)
|
2019-05-29 18:01:52 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
2023-03-02 20:54:17 +00:00
|
|
|
|
my $visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$user->{default_visibility_str},
|
|
|
|
|
$journey->{visibility_str} );
|
|
|
|
|
if (
|
|
|
|
|
$visibility eq 'public'
|
|
|
|
|
or ( $visibility eq 'unlisted'
|
|
|
|
|
and $self->journey_token_ok( $journey, $ts ) )
|
|
|
|
|
or (
|
|
|
|
|
$visibility eq 'travelynx'
|
|
|
|
|
and ( $self->is_user_authenticated
|
|
|
|
|
or $self->journey_token_ok( $journey, $ts ) )
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
my $token = $self->param('token') // q{};
|
|
|
|
|
$self->redirect_to(
|
|
|
|
|
"/p/${name}/j/$journey->{id}?token=${token}-${ts}");
|
2023-03-01 16:59:20 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2023-03-02 20:54:17 +00:00
|
|
|
|
$self->render('not_found');
|
2019-05-29 16:10:45 +00:00
|
|
|
|
}
|
2023-03-01 16:59:20 +00:00
|
|
|
|
return;
|
2019-05-29 16:10:45 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render('not_found');
|
|
|
|
|
return;
|
2020-02-19 19:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my %tw_data = (
|
|
|
|
|
card => 'summary',
|
|
|
|
|
site => '@derfnull',
|
|
|
|
|
image => $self->url_for('/static/icons/icon-512x512.png')
|
|
|
|
|
->to_abs->scheme('https'),
|
|
|
|
|
);
|
2020-09-21 18:31:08 +00:00
|
|
|
|
my %og_data = (
|
|
|
|
|
type => 'article',
|
|
|
|
|
image => $tw_data{image},
|
|
|
|
|
url => $self->url_for("/status/${name}")->to_abs->scheme('https'),
|
|
|
|
|
site_name => 'travelynx',
|
|
|
|
|
);
|
2020-02-19 19:58:38 +00:00
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $visibility;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( $status->{checked_in} or $status->{arr_name} ) {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$user->{default_visibility_str},
|
|
|
|
|
$status->{visibility_str} );
|
|
|
|
|
if (
|
|
|
|
|
not(
|
|
|
|
|
$visibility eq 'public'
|
|
|
|
|
or ( $visibility eq 'unlisted'
|
|
|
|
|
and $self->status_token_ok( $status, $ts ) )
|
|
|
|
|
or (
|
|
|
|
|
$visibility eq 'travelynx'
|
|
|
|
|
and ( $self->is_user_authenticated
|
|
|
|
|
or $self->status_token_ok( $status, $ts ) )
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$status->{checked_in} = 0;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
$status->{arr_name} = undef;
|
2023-02-27 21:14:54 +00:00
|
|
|
|
}
|
2020-02-19 19:58:38 +00:00
|
|
|
|
}
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( not $status->{checked_in}
|
|
|
|
|
and $status->{arr_name}
|
|
|
|
|
and not $user->{past_status} )
|
|
|
|
|
{
|
|
|
|
|
$status->{arr_name} = undef;
|
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
if ( $status->{checked_in} ) {
|
2020-09-21 18:31:08 +00:00
|
|
|
|
$og_data{url} .= '/' . $status->{sched_departure}->epoch;
|
|
|
|
|
$og_data{title} = $tw_data{title} = "${name} ist unterwegs";
|
|
|
|
|
$og_data{description} = $tw_data{description} = sprintf(
|
2020-02-19 19:58:38 +00:00
|
|
|
|
'%s %s von %s nach %s',
|
|
|
|
|
$status->{train_type}, $status->{train_line} // $status->{train_no},
|
2020-04-19 14:49:31 +00:00
|
|
|
|
$status->{dep_name}, $status->{arr_name} // 'irgendwo'
|
2020-02-19 19:58:38 +00:00
|
|
|
|
);
|
|
|
|
|
if ( $status->{real_arrival}->epoch ) {
|
|
|
|
|
$tw_data{description} .= $status->{real_arrival}
|
|
|
|
|
->strftime(' – Ankunft gegen %H:%M Uhr');
|
2020-09-21 18:31:08 +00:00
|
|
|
|
$og_data{description} .= $status->{real_arrival}
|
|
|
|
|
->strftime(' – Ankunft gegen %H:%M Uhr');
|
2019-05-29 16:10:45 +00:00
|
|
|
|
}
|
2020-02-19 19:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2020-09-21 18:31:08 +00:00
|
|
|
|
$og_data{title} = $tw_data{title}
|
|
|
|
|
= "${name} ist gerade nicht eingecheckt";
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$og_data{description} = $tw_data{description} = q{};
|
2020-02-19 19:58:38 +00:00
|
|
|
|
}
|
2019-05-29 16:10:45 +00:00
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'user_status',
|
|
|
|
|
name => $name,
|
|
|
|
|
public_level => $user->{public_level},
|
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $visibility,
|
|
|
|
|
twitter => \%tw_data,
|
|
|
|
|
opengraph => \%og_data,
|
2023-03-04 08:33:44 +00:00
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
2023-02-27 21:14:54 +00:00
|
|
|
|
);
|
2019-04-30 21:23:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 20:27:50 +00:00
|
|
|
|
sub public_profile {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
my $name = $self->stash('name');
|
|
|
|
|
my $user = $self->users->get_privacy_by_name( name => $name );
|
|
|
|
|
|
2023-03-02 17:43:29 +00:00
|
|
|
|
if ( not $user
|
|
|
|
|
or $user->{past_visible} == 0
|
|
|
|
|
or ( $user->{past_visible} == 1 and not $self->is_user_authenticated ) )
|
|
|
|
|
{
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render('not_found');
|
2023-03-02 17:43:29 +00:00
|
|
|
|
return;
|
2023-02-27 21:14:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $status = $self->get_user_status( $user->{id} );
|
|
|
|
|
my $visibility;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( $status->{checked_in} or $status->{arr_name} ) {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$user->{default_visibility_str},
|
|
|
|
|
$status->{visibility_str} );
|
|
|
|
|
if (
|
|
|
|
|
not(
|
|
|
|
|
$visibility eq 'public'
|
|
|
|
|
or ( $visibility eq 'unlisted'
|
|
|
|
|
and $self->status_token_ok($status) )
|
|
|
|
|
or (
|
|
|
|
|
$visibility eq 'travelynx'
|
|
|
|
|
and ( $self->is_user_authenticated
|
|
|
|
|
or $self->status_token_ok($status) )
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$status->{checked_in} = 0;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
$status->{arr_name} = undef;
|
2023-02-27 21:14:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( not $status->{checked_in}
|
|
|
|
|
and $status->{arr_name}
|
|
|
|
|
and not $user->{past_status} )
|
|
|
|
|
{
|
|
|
|
|
$status->{arr_name} = undef;
|
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
my %opt = (
|
|
|
|
|
uid => $user->{id},
|
|
|
|
|
limit => 10,
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ( not $user->{past_all} ) {
|
|
|
|
|
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
|
|
|
|
$opt{before} = DateTime->now( time_zone => 'Europe/Berlin' );
|
|
|
|
|
$opt{after} = $now->clone->subtract( weeks => 4 );
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 20:27:50 +00:00
|
|
|
|
if (
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$user->{default_visibility_str} eq 'public'
|
|
|
|
|
or ( $user->{default_visibility_str} eq 'travelynx'
|
|
|
|
|
and $self->is_user_authenticated )
|
2020-09-03 20:27:50 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$opt{with_default_visibility} = 1;
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$opt{with_default_visibility} = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $self->is_user_authenticated ) {
|
|
|
|
|
$opt{min_visibility} = 'travelynx';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$opt{min_visibility} = 'public';
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
my @journeys = $self->journeys->get(%opt);
|
|
|
|
|
|
|
|
|
|
$self->render(
|
|
|
|
|
'profile',
|
|
|
|
|
name => $name,
|
|
|
|
|
uid => $user->{id},
|
|
|
|
|
public_level => $user->{public_level},
|
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $visibility,
|
|
|
|
|
journeys => [@journeys],
|
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
|
|
|
|
);
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub public_journey_details {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $name = $self->stash('name');
|
|
|
|
|
my $journey_id = $self->stash('id');
|
|
|
|
|
my $user = $self->users->get_privacy_by_name( name => $name );
|
|
|
|
|
|
|
|
|
|
$self->param( journey_id => $journey_id );
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
if ( not( $user and $journey_id and $journey_id =~ m{ ^ \d+ $ }x ) ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
|
|
|
|
status => 404,
|
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $journey = $self->journeys->get_single(
|
|
|
|
|
uid => $user->{id},
|
|
|
|
|
journey_id => $journey_id,
|
|
|
|
|
verbose => 1,
|
|
|
|
|
with_datetime => 1,
|
|
|
|
|
with_polyline => 1,
|
|
|
|
|
with_visibility => 1,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ( not $journey ) {
|
2020-09-03 20:27:50 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
|
|
|
|
status => 404,
|
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 17:31:17 +00:00
|
|
|
|
my $is_past;
|
|
|
|
|
if ( not $user->{past_all} ) {
|
|
|
|
|
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
|
|
|
|
if ( $journey->{sched_dep_ts} < $now->subtract( weeks => 4 )->epoch ) {
|
|
|
|
|
$is_past = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $visibility
|
|
|
|
|
= $self->compute_effective_visibility( $user->{default_visibility_str},
|
|
|
|
|
$journey->{visibility_str} );
|
|
|
|
|
|
2020-09-03 20:27:50 +00:00
|
|
|
|
if (
|
2023-02-27 21:14:54 +00:00
|
|
|
|
not(
|
2023-03-02 17:31:17 +00:00
|
|
|
|
( $visibility eq 'public' and not $is_past )
|
2023-02-27 21:14:54 +00:00
|
|
|
|
or ( $visibility eq 'unlisted'
|
2023-02-28 18:45:27 +00:00
|
|
|
|
and $self->journey_token_ok($journey) )
|
2023-02-27 21:14:54 +00:00
|
|
|
|
or (
|
|
|
|
|
$visibility eq 'travelynx'
|
2023-03-02 17:31:17 +00:00
|
|
|
|
and ( ( $self->is_user_authenticated and not $is_past )
|
2023-02-28 18:45:27 +00:00
|
|
|
|
or $self->journey_token_ok($journey) )
|
2023-02-27 21:14:54 +00:00
|
|
|
|
)
|
2022-02-20 20:45:51 +00:00
|
|
|
|
)
|
2020-09-03 20:27:50 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
|
|
|
|
status => 404,
|
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
2020-09-03 20:27:50 +00:00
|
|
|
|
);
|
2023-02-27 21:14:54 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-09-03 20:27:50 +00:00
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $title = sprintf( 'Fahrt von %s nach %s am %s',
|
|
|
|
|
$journey->{from_name}, $journey->{to_name},
|
|
|
|
|
$journey->{rt_arrival}->strftime('%d.%m.%Y') );
|
|
|
|
|
my $delay = 'pünktlich ';
|
|
|
|
|
if ( $journey->{rt_arrival} != $journey->{sched_arrival} ) {
|
|
|
|
|
$delay = sprintf(
|
|
|
|
|
'mit %+d ',
|
|
|
|
|
(
|
|
|
|
|
$journey->{rt_arrival}->epoch
|
|
|
|
|
- $journey->{sched_arrival}->epoch
|
|
|
|
|
) / 60
|
|
|
|
|
);
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $description = sprintf( 'Ankunft mit %s %s %s',
|
|
|
|
|
$journey->{type}, $journey->{no},
|
|
|
|
|
$journey->{rt_arrival}->strftime('um %H:%M') );
|
|
|
|
|
if ( $journey->{km_route} > 0.1 ) {
|
|
|
|
|
$description = sprintf( '%.0f km mit %s %s – Ankunft %sum %s',
|
|
|
|
|
$journey->{km_route}, $journey->{type}, $journey->{no},
|
|
|
|
|
$delay, $journey->{rt_arrival}->strftime('%H:%M') );
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my %tw_data = (
|
|
|
|
|
card => 'summary',
|
|
|
|
|
site => '@derfnull',
|
|
|
|
|
image => $self->url_for('/static/icons/icon-512x512.png')
|
|
|
|
|
->to_abs->scheme('https'),
|
|
|
|
|
title => $title,
|
|
|
|
|
description => $description,
|
|
|
|
|
);
|
|
|
|
|
my %og_data = (
|
|
|
|
|
type => 'article',
|
|
|
|
|
image => $tw_data{image},
|
|
|
|
|
url => $self->url_for->to_abs,
|
|
|
|
|
site_name => 'travelynx',
|
|
|
|
|
title => $title,
|
|
|
|
|
description => $description,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
my $map_data = $self->journeys_to_map_data(
|
|
|
|
|
journeys => [$journey],
|
|
|
|
|
include_manual => 1,
|
|
|
|
|
);
|
|
|
|
|
if ( $journey->{user_data}{comment}
|
2023-03-02 17:34:01 +00:00
|
|
|
|
and not $user->{comments_visible} )
|
2023-02-27 21:14:54 +00:00
|
|
|
|
{
|
|
|
|
|
delete $journey->{user_data}{comment};
|
|
|
|
|
}
|
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
|
|
|
|
error => undef,
|
|
|
|
|
journey => $journey,
|
|
|
|
|
with_map => 1,
|
|
|
|
|
username => $name,
|
|
|
|
|
readonly => 1,
|
|
|
|
|
twitter => \%tw_data,
|
|
|
|
|
opengraph => \%og_data,
|
|
|
|
|
journey_visibility => $visibility,
|
|
|
|
|
%{$map_data},
|
|
|
|
|
);
|
2020-09-03 20:27:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-30 21:23:49 +00:00
|
|
|
|
sub public_status_card {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
my $name = $self->stash('name');
|
2022-02-20 20:45:51 +00:00
|
|
|
|
$name =~ s{[.]html$}{};
|
2020-07-27 16:53:22 +00:00
|
|
|
|
my $user = $self->users->get_privacy_by_name( name => $name );
|
2019-04-30 21:23:49 +00:00
|
|
|
|
|
2019-05-02 08:05:15 +00:00
|
|
|
|
delete $self->stash->{layout};
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
if ( not $user ) {
|
2019-04-30 21:23:49 +00:00
|
|
|
|
$self->render('not_found');
|
2023-02-27 21:14:54 +00:00
|
|
|
|
return;
|
2019-04-30 21:23:49 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
my $status = $self->get_user_status( $user->{id} );
|
|
|
|
|
my $visibility;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( $status->{checked_in} or $status->{arr_name} ) {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$user->{default_visibility_str},
|
|
|
|
|
$status->{visibility_str} );
|
|
|
|
|
if (
|
|
|
|
|
not(
|
|
|
|
|
$visibility eq 'public'
|
|
|
|
|
or ( $visibility eq 'unlisted'
|
|
|
|
|
and $self->status_token_ok($status) )
|
|
|
|
|
or (
|
|
|
|
|
$visibility eq 'travelynx'
|
|
|
|
|
and ( $self->is_user_authenticated
|
|
|
|
|
or $self->status_token_ok($status) )
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$status->{checked_in} = 0;
|
2023-03-04 08:03:50 +00:00
|
|
|
|
$status->{arr_name} = undef;
|
2023-02-27 21:14:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-04 08:03:50 +00:00
|
|
|
|
if ( not $status->{checked_in}
|
|
|
|
|
and $status->{arr_name}
|
|
|
|
|
and not $user->{past_status} )
|
|
|
|
|
{
|
|
|
|
|
$status->{arr_name} = undef;
|
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
$self->render(
|
|
|
|
|
'_public_status_card',
|
|
|
|
|
name => $name,
|
|
|
|
|
public_level => $user->{public_level},
|
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $visibility,
|
|
|
|
|
);
|
2019-04-30 21:23:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 20:27:13 +00:00
|
|
|
|
sub status_card {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $status = $self->get_user_status;
|
|
|
|
|
|
|
|
|
|
delete $self->stash->{layout};
|
|
|
|
|
|
|
|
|
|
if ( $status->{checked_in} ) {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $journey_visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$self->current_user->{default_visibility_str},
|
|
|
|
|
$status->{visibility_str} );
|
2022-07-27 11:54:20 +00:00
|
|
|
|
if ( defined $status->{arrival_countdown}
|
2022-08-26 16:38:54 +00:00
|
|
|
|
and $status->{arrival_countdown} < ( 40 * 60 ) )
|
2022-07-27 11:54:20 +00:00
|
|
|
|
{
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render_later;
|
|
|
|
|
$self->get_connecting_trains_p->then(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ( $connecting_trains, $transit_fyi ) = @_;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'_checked_in',
|
2023-02-27 21:14:54 +00:00
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
|
|
|
|
connections => $connecting_trains,
|
|
|
|
|
transit_fyi => $transit_fyi
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'_checked_in',
|
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
|
|
|
|
);
|
2022-08-01 08:07:24 +00:00
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
return;
|
2022-07-27 11:54:20 +00:00
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'_checked_in',
|
|
|
|
|
journey => $status,
|
|
|
|
|
journey_visibility => $journey_visibility,
|
|
|
|
|
);
|
2019-04-23 20:27:13 +00:00
|
|
|
|
}
|
2020-02-17 20:13:07 +00:00
|
|
|
|
elsif ( $status->{cancellation} ) {
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render_later;
|
|
|
|
|
$self->get_connecting_trains_p(
|
2022-07-26 12:51:57 +00:00
|
|
|
|
eva => $status->{cancellation}{dep_eva},
|
|
|
|
|
destination_name => $status->{cancellation}{arr_name}
|
2022-08-01 08:07:24 +00:00
|
|
|
|
)->then(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ($connecting_trains) = @_;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'_cancelled_departure',
|
|
|
|
|
journey => $status->{cancellation},
|
2022-09-24 14:11:51 +00:00
|
|
|
|
connections => $connecting_trains
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
$self->render( '_cancelled_departure',
|
|
|
|
|
journey => $status->{cancellation} );
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
return;
|
2020-02-17 20:13:07 +00:00
|
|
|
|
}
|
2019-04-23 20:27:13 +00:00
|
|
|
|
else {
|
2022-07-26 13:36:22 +00:00
|
|
|
|
my @connecting_trains;
|
|
|
|
|
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
|
|
|
|
if ( $now->epoch - $status->{timestamp}->epoch < ( 30 * 60 ) ) {
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render_later;
|
|
|
|
|
$self->get_connecting_trains_p->then(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ($connecting_trains) = @_;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'_checked_out',
|
|
|
|
|
journey => $status,
|
2022-09-24 14:11:51 +00:00
|
|
|
|
connections => $connecting_trains
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
$self->render( '_checked_out', journey => $status );
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
return;
|
2022-07-26 13:36:22 +00:00
|
|
|
|
}
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render( '_checked_out', journey => $status );
|
2019-04-23 20:27:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 21:12:45 +00:00
|
|
|
|
sub geolocation {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
my $lon = $self->param('lon');
|
|
|
|
|
my $lat = $self->param('lat');
|
|
|
|
|
|
|
|
|
|
if ( not $lon or not $lat ) {
|
|
|
|
|
$self->render( json => { error => 'Invalid lon/lat received' } );
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
my @candidates = map {
|
|
|
|
|
{
|
|
|
|
|
ds100 => $_->[0][0],
|
|
|
|
|
name => $_->[0][1],
|
|
|
|
|
eva => $_->[0][2],
|
|
|
|
|
lon => $_->[0][3],
|
|
|
|
|
lat => $_->[0][4],
|
|
|
|
|
distance => $_->[1],
|
|
|
|
|
}
|
|
|
|
|
} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon,
|
2019-07-20 13:42:11 +00:00
|
|
|
|
$lat, 10 );
|
|
|
|
|
@candidates = uniq_by { $_->{name} } @candidates;
|
2019-10-19 14:47:33 +00:00
|
|
|
|
if ( @candidates > 5 ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
candidates => [ @candidates[ 0 .. 4 ] ],
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
candidates => [@candidates],
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-03-21 21:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 20:22:10 +00:00
|
|
|
|
sub travel_action {
|
2019-03-21 21:12:45 +00:00
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $params = $self->req->json;
|
|
|
|
|
|
|
|
|
|
if ( not exists $params->{action} ) {
|
|
|
|
|
$params = $self->req->params->to_hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( not $self->is_user_authenticated ) {
|
|
|
|
|
|
|
|
|
|
# We deliberately do not set the HTTP status for these replies, as it
|
|
|
|
|
# confuses jquery.
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => 'Session error, please login again',
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( not $params->{action} ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => 'Missing action value',
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $station = $params->{station};
|
|
|
|
|
|
|
|
|
|
if ( $params->{action} eq 'checkin' ) {
|
|
|
|
|
|
2020-09-30 17:12:29 +00:00
|
|
|
|
my ( $train, $error ) = $self->checkin(
|
|
|
|
|
station => $params->{station},
|
|
|
|
|
train_id => $params->{train}
|
|
|
|
|
);
|
2019-05-19 08:32:57 +00:00
|
|
|
|
my $destination = $params->{dest};
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-05-19 08:32:57 +00:00
|
|
|
|
elsif ( not $destination ) {
|
2019-03-21 21:12:45 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => '/',
|
2019-03-21 21:12:45 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-05-19 08:32:57 +00:00
|
|
|
|
else {
|
|
|
|
|
# Silently ignore errors -- if they are permanent, the user will see
|
|
|
|
|
# them when selecting the destination manually.
|
2020-09-30 17:12:29 +00:00
|
|
|
|
my ( $still_checked_in, undef ) = $self->checkout(
|
|
|
|
|
station => $destination,
|
|
|
|
|
force => 0
|
|
|
|
|
);
|
2019-05-19 08:32:57 +00:00
|
|
|
|
my $station_link = '/s/' . $destination;
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => $still_checked_in ? '/' : $station_link,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-03-21 21:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
elsif ( $params->{action} eq 'checkout' ) {
|
2020-09-30 17:12:29 +00:00
|
|
|
|
my ( $still_checked_in, $error ) = $self->checkout(
|
|
|
|
|
station => $params->{station},
|
|
|
|
|
force => $params->{force}
|
|
|
|
|
);
|
2019-04-23 16:08:07 +00:00
|
|
|
|
my $station_link = '/s/' . $params->{station};
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => $still_checked_in ? '/' : $station_link,
|
2019-03-21 21:12:45 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif ( $params->{action} eq 'undo' ) {
|
2019-04-25 07:22:54 +00:00
|
|
|
|
my $status = $self->get_user_status;
|
2019-04-26 17:53:01 +00:00
|
|
|
|
my $error = $self->undo( $params->{undo_id} );
|
2019-03-21 21:12:45 +00:00
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-04-25 07:22:54 +00:00
|
|
|
|
my $redir = '/';
|
2019-04-26 17:53:01 +00:00
|
|
|
|
if ( $status->{checked_in} or $status->{cancelled} ) {
|
2019-04-25 07:22:54 +00:00
|
|
|
|
$redir = '/s/' . $status->{dep_ds100};
|
|
|
|
|
}
|
2019-03-21 21:12:45 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
2019-04-25 07:22:54 +00:00
|
|
|
|
redirect_to => $redir,
|
2019-03-21 21:12:45 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif ( $params->{action} eq 'cancelled_from' ) {
|
2020-09-30 17:12:29 +00:00
|
|
|
|
my ( undef, $error ) = $self->checkin(
|
|
|
|
|
station => $params->{station},
|
|
|
|
|
train_id => $params->{train}
|
|
|
|
|
);
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => '/',
|
2019-03-21 21:12:45 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif ( $params->{action} eq 'cancelled_to' ) {
|
2020-09-30 17:12:29 +00:00
|
|
|
|
my ( undef, $error ) = $self->checkout(
|
|
|
|
|
station => $params->{station},
|
|
|
|
|
force => 1
|
|
|
|
|
);
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => '/',
|
2019-03-21 21:12:45 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-04 16:26:53 +00:00
|
|
|
|
elsif ( $params->{action} eq 'delete' ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my $error = $self->journeys->delete(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
id => $params->{id},
|
|
|
|
|
checkin => $params->{checkin},
|
|
|
|
|
checkout => $params->{checkout}
|
|
|
|
|
);
|
2019-04-04 16:26:53 +00:00
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => $error,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
success => 1,
|
|
|
|
|
redirect_to => '/history',
|
2019-04-04 16:26:53 +00:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-21 21:12:45 +00:00
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
json => {
|
|
|
|
|
success => 0,
|
|
|
|
|
error => 'invalid action value',
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub station {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $station = $self->stash('station');
|
|
|
|
|
my $train = $self->param('train');
|
|
|
|
|
|
2022-07-26 08:41:44 +00:00
|
|
|
|
$self->render_later;
|
|
|
|
|
$self->iris->get_departures_p(
|
2020-08-06 14:04:12 +00:00
|
|
|
|
station => $station,
|
|
|
|
|
lookbehind => 120,
|
|
|
|
|
lookahead => 30,
|
|
|
|
|
with_related => 1
|
2022-07-26 08:41:44 +00:00
|
|
|
|
)->then(
|
|
|
|
|
sub {
|
|
|
|
|
my ($status) = @_;
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
2022-07-26 08:41:44 +00:00
|
|
|
|
# You can't check into a train which terminates here
|
|
|
|
|
my @results = grep { $_->departure } @{ $status->{results} };
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
2022-07-26 08:41:44 +00:00
|
|
|
|
@results = map { $_->[0] }
|
|
|
|
|
sort { $b->[1] <=> $a->[1] }
|
|
|
|
|
map { [ $_, $_->departure->epoch // $_->sched_departure->epoch ] }
|
|
|
|
|
@results;
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
my $connections_p;
|
2022-07-26 08:41:44 +00:00
|
|
|
|
if ($train) {
|
|
|
|
|
@results
|
|
|
|
|
= grep { $_->type . ' ' . $_->train_no eq $train } @results;
|
|
|
|
|
}
|
2022-07-26 11:19:29 +00:00
|
|
|
|
else {
|
2022-07-26 12:51:57 +00:00
|
|
|
|
my $user = $self->get_user_status;
|
2022-07-26 13:36:45 +00:00
|
|
|
|
if ( $user->{cancellation}
|
|
|
|
|
and $status->{station_eva} eq
|
|
|
|
|
$user->{cancellation}{dep_eva} )
|
|
|
|
|
{
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$connections_p = $self->get_connecting_trains_p(
|
2022-07-26 12:51:57 +00:00
|
|
|
|
eva => $user->{cancellation}{dep_eva},
|
|
|
|
|
destination_name => $user->{cancellation}{arr_name}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$connections_p = $self->get_connecting_trains_p(
|
2022-07-26 12:51:57 +00:00
|
|
|
|
eva => $status->{station_eva} );
|
|
|
|
|
}
|
2022-07-26 11:19:29 +00:00
|
|
|
|
}
|
2019-03-21 21:12:45 +00:00
|
|
|
|
|
2022-08-01 08:07:24 +00:00
|
|
|
|
if ($connections_p) {
|
|
|
|
|
$connections_p->then(
|
|
|
|
|
sub {
|
2022-09-24 14:11:51 +00:00
|
|
|
|
my ($connecting_trains) = @_;
|
2022-08-01 08:07:24 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'departures',
|
|
|
|
|
eva => $status->{station_eva},
|
|
|
|
|
results => \@results,
|
|
|
|
|
station => $status->{station_name},
|
|
|
|
|
related_stations => $status->{related_stations},
|
2022-09-24 14:11:51 +00:00
|
|
|
|
connections => $connecting_trains,
|
2022-09-23 17:25:17 +00:00
|
|
|
|
title => "travelynx: $status->{station_name}",
|
|
|
|
|
version => $self->app->config->{version}
|
|
|
|
|
// 'UNKNOWN',
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
|
|
|
|
$self->render(
|
|
|
|
|
'departures',
|
|
|
|
|
eva => $status->{station_eva},
|
|
|
|
|
results => \@results,
|
|
|
|
|
station => $status->{station_name},
|
|
|
|
|
related_stations => $status->{related_stations},
|
2022-09-23 17:25:17 +00:00
|
|
|
|
title => "travelynx: $status->{station_name}",
|
|
|
|
|
version => $self->app->config->{version}
|
|
|
|
|
// 'UNKNOWN',
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
)->wait;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
'departures',
|
|
|
|
|
eva => $status->{station_eva},
|
|
|
|
|
results => \@results,
|
|
|
|
|
station => $status->{station_name},
|
|
|
|
|
related_stations => $status->{related_stations},
|
|
|
|
|
title => "travelynx: $status->{station_name}",
|
2022-09-23 17:25:17 +00:00
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
2022-08-01 08:07:24 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
2022-07-26 08:41:44 +00:00
|
|
|
|
}
|
|
|
|
|
)->catch(
|
|
|
|
|
sub {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
my ($err) = @_;
|
|
|
|
|
if ( ref($err) eq 'HASH' ) {
|
2022-07-26 11:19:29 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'landingpage',
|
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
|
|
|
|
with_autocomplete => 1,
|
|
|
|
|
with_geolocation => 1,
|
2022-11-09 17:14:07 +00:00
|
|
|
|
error => $err->{errstr},
|
2022-07-26 11:19:29 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-11-09 17:14:07 +00:00
|
|
|
|
$self->render( 'exception', exception => $err );
|
2022-07-26 11:19:29 +00:00
|
|
|
|
}
|
2022-07-26 08:41:44 +00:00
|
|
|
|
}
|
|
|
|
|
)->wait;
|
2020-07-27 16:53:22 +00:00
|
|
|
|
$self->users->mark_seen( uid => $self->current_user->{id} );
|
2019-03-21 21:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub redirect_to_station {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $station = $self->param('station');
|
|
|
|
|
|
|
|
|
|
$self->redirect_to("/s/${station}");
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 16:44:33 +00:00
|
|
|
|
sub cancelled {
|
2019-03-22 15:56:49 +00:00
|
|
|
|
my ($self) = @_;
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my @journeys = $self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
2019-11-24 19:29:52 +00:00
|
|
|
|
cancelled => 1,
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
);
|
2019-03-27 20:20:59 +00:00
|
|
|
|
|
2019-03-22 15:56:49 +00:00
|
|
|
|
$self->respond_to(
|
2019-03-27 20:20:59 +00:00
|
|
|
|
json => { json => [@journeys] },
|
|
|
|
|
any => {
|
2019-04-07 16:44:33 +00:00
|
|
|
|
template => 'cancelled',
|
2019-03-27 20:20:59 +00:00
|
|
|
|
journeys => [@journeys]
|
|
|
|
|
}
|
2019-03-22 15:56:49 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 16:44:33 +00:00
|
|
|
|
sub history {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
$self->render( template => 'history' );
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 14:49:31 +00:00
|
|
|
|
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 );
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my @journeys = $self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
2020-04-19 14:49:31 +00:00
|
|
|
|
after => $interval_start,
|
|
|
|
|
before => $interval_end,
|
|
|
|
|
with_datetime => 1,
|
|
|
|
|
);
|
|
|
|
|
|
2020-04-19 15:40:39 +00:00
|
|
|
|
if ( not $station ) {
|
|
|
|
|
my %candidate_count;
|
|
|
|
|
for my $journey (@journeys) {
|
|
|
|
|
my $dep = $journey->{rt_departure};
|
|
|
|
|
my $arr = $journey->{rt_arrival};
|
|
|
|
|
if ( $arr->dow <= 5 and $arr->hour <= 12 ) {
|
|
|
|
|
$candidate_count{ $journey->{to_name} }++;
|
|
|
|
|
}
|
|
|
|
|
elsif ( $dep->dow <= 5 and $dep->hour > 12 ) {
|
|
|
|
|
$candidate_count{ $journey->{from_name} }++;
|
|
|
|
|
}
|
2020-04-19 15:49:14 +00:00
|
|
|
|
else {
|
|
|
|
|
# Avoid selecting an intermediate station for multi-leg commutes.
|
|
|
|
|
# Assumption: The intermediate station is also used for private
|
|
|
|
|
# travels -> penalize stations which are used on weekends or at
|
|
|
|
|
# unexpected times.
|
|
|
|
|
$candidate_count{ $journey->{from_name} }--;
|
|
|
|
|
$candidate_count{ $journey->{to_name} }--;
|
|
|
|
|
}
|
2020-04-19 15:40:39 +00:00
|
|
|
|
}
|
|
|
|
|
$station = max_by { $candidate_count{$_} } keys %candidate_count;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 14:49:31 +00:00
|
|
|
|
my %journeys_by_month;
|
2020-04-19 15:03:02 +00:00
|
|
|
|
my %count_by_month;
|
2020-04-19 14:49:31 +00:00
|
|
|
|
my $total = 0;
|
|
|
|
|
|
2020-04-19 15:03:02 +00:00
|
|
|
|
my $prev_doy = 0;
|
2020-04-19 14:49:31 +00:00
|
|
|
|
for my $journey ( reverse @journeys ) {
|
|
|
|
|
my $month = $journey->{rt_departure}->month;
|
|
|
|
|
if (
|
2020-04-19 15:03:02 +00:00
|
|
|
|
(
|
|
|
|
|
$filter_type eq 'exact' and ( $journey->{to_name} eq $station
|
|
|
|
|
or $journey->{from_name} eq $station )
|
|
|
|
|
)
|
|
|
|
|
or (
|
|
|
|
|
$filter_type eq 'substring'
|
|
|
|
|
and ( $journey->{to_name} =~ m{\Q$station\E}
|
|
|
|
|
or $journey->{from_name} =~ m{\Q$station\E} )
|
|
|
|
|
)
|
|
|
|
|
or (
|
|
|
|
|
$filter_type eq 'regex'
|
|
|
|
|
and ( $journey->{to_name} =~ m{$station}
|
|
|
|
|
or $journey->{from_name} =~ m{$station} )
|
|
|
|
|
)
|
2020-04-19 14:49:31 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
push( @{ $journeys_by_month{$month} }, $journey );
|
2020-04-19 15:03:02 +00:00
|
|
|
|
|
|
|
|
|
my $doy = $journey->{rt_departure}->day_of_year;
|
|
|
|
|
if ( $doy != $prev_doy ) {
|
|
|
|
|
$count_by_month{$month}++;
|
|
|
|
|
$total++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$prev_doy = $doy;
|
2020-04-19 14:49:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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,
|
2020-04-19 15:03:02 +00:00
|
|
|
|
count_by_month => \%count_by_month,
|
2020-04-19 14:49:31 +00:00
|
|
|
|
total_journeys => $total,
|
|
|
|
|
months => [
|
|
|
|
|
qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember)
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-15 23:01:50 +00:00
|
|
|
|
sub map_history {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
2019-11-16 14:24:16 +00:00
|
|
|
|
my $location = $self->app->coordinates_by_station;
|
2019-11-15 23:01:50 +00:00
|
|
|
|
|
2020-01-31 17:16:00 +00:00
|
|
|
|
if ( not $self->param('route_type') ) {
|
|
|
|
|
$self->param( route_type => 'polybee' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $route_type = $self->param('route_type');
|
2022-04-02 13:24:39 +00:00
|
|
|
|
my $filter_from = $self->param('filter_after');
|
|
|
|
|
my $filter_until = $self->param('filter_before');
|
2022-04-02 16:14:21 +00:00
|
|
|
|
my $filter_type = $self->param('filter_type');
|
2020-01-31 17:16:00 +00:00
|
|
|
|
my $with_polyline = $route_type eq 'beeline' ? 0 : 1;
|
2020-01-29 17:48:58 +00:00
|
|
|
|
|
2022-04-02 13:24:39 +00:00
|
|
|
|
my $parser = DateTime::Format::Strptime->new(
|
|
|
|
|
pattern => '%d.%m.%Y',
|
|
|
|
|
locale => 'de_DE',
|
|
|
|
|
time_zone => 'Europe/Berlin'
|
|
|
|
|
);
|
|
|
|
|
|
2022-09-07 16:08:27 +00:00
|
|
|
|
if ( $filter_from
|
|
|
|
|
and $filter_from =~ m{ ^ (\d+) [.] (\d+) [.] (\d+) $ }x )
|
2022-04-02 13:24:39 +00:00
|
|
|
|
{
|
|
|
|
|
$filter_from = $parser->parse_datetime($filter_from);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$filter_from = undef;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $filter_until
|
|
|
|
|
and $filter_until =~ m{ ^ (\d+) [.] (\d+) [.] (\d+) $ }x )
|
|
|
|
|
{
|
|
|
|
|
$filter_until = $parser->parse_datetime($filter_until);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$filter_until = undef;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my @journeys = $self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
2022-04-02 13:24:39 +00:00
|
|
|
|
with_polyline => $with_polyline,
|
|
|
|
|
after => $filter_from,
|
|
|
|
|
before => $filter_until,
|
2020-08-16 19:20:23 +00:00
|
|
|
|
);
|
2019-11-15 23:01:50 +00:00
|
|
|
|
|
2022-04-02 16:14:21 +00:00
|
|
|
|
if ($filter_type) {
|
|
|
|
|
my @filter = split( qr{, *}, $filter_type );
|
2022-09-07 16:08:27 +00:00
|
|
|
|
@journeys
|
|
|
|
|
= grep { has_str_in_list( $_->{type}, @filter ) } @journeys;
|
2022-04-02 16:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-16 22:47:18 +00:00
|
|
|
|
if ( not @journeys ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
template => 'history_map',
|
|
|
|
|
with_map => 1,
|
2020-01-25 13:55:51 +00:00
|
|
|
|
skipped_journeys => [],
|
2019-11-16 22:47:18 +00:00
|
|
|
|
station_coordinates => [],
|
2020-01-25 13:41:33 +00:00
|
|
|
|
polyline_groups => [],
|
2019-11-16 22:47:18 +00:00
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-27 17:26:15 +00:00
|
|
|
|
my $include_manual = $self->param('include_manual') ? 1 : 0;
|
|
|
|
|
|
2020-02-23 17:35:56 +00:00
|
|
|
|
my $res = $self->journeys_to_map_data(
|
|
|
|
|
journeys => \@journeys,
|
|
|
|
|
route_type => $route_type,
|
|
|
|
|
include_manual => $include_manual
|
|
|
|
|
);
|
2020-01-29 17:48:58 +00:00
|
|
|
|
|
2019-11-15 23:01:50 +00:00
|
|
|
|
$self->render(
|
2020-02-23 17:35:56 +00:00
|
|
|
|
template => 'history_map',
|
|
|
|
|
with_map => 1,
|
|
|
|
|
%{$res}
|
2019-11-15 23:01:50 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 15:56:49 +00:00
|
|
|
|
sub json_history {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
json => [ $self->journeys->get( uid => $self->current_user->{id} ) ] );
|
2019-04-07 16:44:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 16:26:20 +00:00
|
|
|
|
sub csv_history {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
|
|
my $csv = Text::CSV->new( { eol => "\r\n" } );
|
|
|
|
|
my $buf = q{};
|
|
|
|
|
|
|
|
|
|
$csv->combine(
|
|
|
|
|
qw(Zugtyp Linie Nummer Start Ziel),
|
|
|
|
|
'Start (DS100)',
|
|
|
|
|
'Ziel (DS100)',
|
|
|
|
|
'Abfahrt (soll)',
|
|
|
|
|
'Abfahrt (ist)',
|
|
|
|
|
'Ankunft (soll)',
|
|
|
|
|
'Ankunft (ist)',
|
|
|
|
|
'Kommentar',
|
|
|
|
|
'ID'
|
|
|
|
|
);
|
|
|
|
|
$buf .= $csv->string;
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
for my $journey (
|
|
|
|
|
$self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
2020-04-19 16:26:20 +00:00
|
|
|
|
if (
|
|
|
|
|
$csv->combine(
|
|
|
|
|
$journey->{type},
|
|
|
|
|
$journey->{line},
|
|
|
|
|
$journey->{no},
|
|
|
|
|
$journey->{from_name},
|
|
|
|
|
$journey->{to_name},
|
|
|
|
|
$journey->{from_ds100},
|
|
|
|
|
$journey->{to_ds100},
|
|
|
|
|
$journey->{sched_departure}->strftime('%Y-%m-%d %H:%M'),
|
|
|
|
|
$journey->{rt_departure}->strftime('%Y-%m-%d %H:%M'),
|
|
|
|
|
$journey->{sched_arrival}->strftime('%Y-%m-%d %H:%M'),
|
|
|
|
|
$journey->{rt_arrival}->strftime('%Y-%m-%d %H:%M'),
|
|
|
|
|
$journey->{user_data}{comment} // q{},
|
|
|
|
|
$journey->{id}
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$buf .= $csv->string;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$self->render(
|
|
|
|
|
text => $buf,
|
|
|
|
|
format => 'csv'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 10:07:16 +00:00
|
|
|
|
sub year_in_review {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $year = $self->stash('year');
|
|
|
|
|
my @journeys;
|
|
|
|
|
|
|
|
|
|
# DateTime is very slow when looking far into the future due to DST changes
|
|
|
|
|
# -> Limit time range to avoid accidental DoS.
|
|
|
|
|
if ( not( $year =~ m{ ^ [0-9]{4} $ }x and $year > 1990 and $year < 2100 ) )
|
|
|
|
|
{
|
|
|
|
|
$self->render('not_found');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
@journeys = $self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
after => $interval_start,
|
|
|
|
|
before => $interval_end,
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ( not @journeys ) {
|
|
|
|
|
$self->render( 'not_found',
|
|
|
|
|
message => 'Keine Zugfahrten im angefragten Jahr gefunden.' );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $now = $self->now;
|
|
|
|
|
if (
|
|
|
|
|
not( $year < $now->year or ( $now->month == 12 and $now->day == 31 ) ) )
|
|
|
|
|
{
|
|
|
|
|
$self->render( 'not_found',
|
|
|
|
|
message =>
|
|
|
|
|
'Der aktuelle Jahresrückblick wird erst zum Jahresende (am 31.12.) freigeschaltet'
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my ( $stats, $review ) = $self->journeys->get_stats(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
year => $year,
|
|
|
|
|
review => 1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$self->render(
|
|
|
|
|
'year_in_review',
|
2022-12-30 15:46:56 +00:00
|
|
|
|
version => $self->app->config->{version} // 'UNKNOWN',
|
|
|
|
|
title => "travelynx Jahresrückblick $year",
|
|
|
|
|
year => $year,
|
|
|
|
|
stats => $stats,
|
|
|
|
|
review => $review,
|
2022-12-27 10:07:16 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 16:44:33 +00:00
|
|
|
|
sub yearly_history {
|
|
|
|
|
my ($self) = @_;
|
2022-12-29 13:14:12 +00:00
|
|
|
|
my $year = $self->stash('year');
|
|
|
|
|
my $filter = $self->param('filter');
|
2019-04-07 16:44:33 +00:00
|
|
|
|
my @journeys;
|
|
|
|
|
|
2019-05-10 23:35:57 +00:00
|
|
|
|
# DateTime is very slow when looking far into the future due to DST changes
|
|
|
|
|
# -> Limit time range to avoid accidental DoS.
|
|
|
|
|
if ( not( $year =~ m{ ^ [0-9]{4} $ }x and $year > 1990 and $year < 2100 ) )
|
|
|
|
|
{
|
2022-12-26 14:35:33 +00:00
|
|
|
|
$self->render('not_found');
|
|
|
|
|
return;
|
2019-04-07 16:44:33 +00:00
|
|
|
|
}
|
2022-12-26 14:35:33 +00:00
|
|
|
|
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 );
|
|
|
|
|
@journeys = $self->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
after => $interval_start,
|
|
|
|
|
before => $interval_end,
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
);
|
2022-12-26 15:50:39 +00:00
|
|
|
|
|
2022-12-29 13:14:12 +00:00
|
|
|
|
if ( $filter and $filter eq 'single' ) {
|
|
|
|
|
@journeys = $self->journeys->grep_single(@journeys);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-26 15:50:39 +00:00
|
|
|
|
if ( not @journeys ) {
|
|
|
|
|
$self->render( 'not_found',
|
|
|
|
|
message => 'Keine Zugfahrten im angefragten Jahr gefunden.' );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 10:07:16 +00:00
|
|
|
|
my $stats = $self->journeys->get_stats(
|
2022-12-26 14:35:33 +00:00
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
year => $year
|
|
|
|
|
);
|
2019-04-07 16:44:33 +00:00
|
|
|
|
|
2022-12-27 10:07:16 +00:00
|
|
|
|
my $with_review;
|
|
|
|
|
my $now = $self->now;
|
|
|
|
|
if ( $year < $now->year or ( $now->month == 12 and $now->day == 31 ) ) {
|
|
|
|
|
$with_review = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 16:44:33 +00:00
|
|
|
|
$self->respond_to(
|
|
|
|
|
json => {
|
|
|
|
|
json => {
|
|
|
|
|
journeys => [@journeys],
|
|
|
|
|
statistics => $stats
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
any => {
|
2022-12-27 10:07:16 +00:00
|
|
|
|
template => 'history_by_year',
|
|
|
|
|
journeys => [@journeys],
|
|
|
|
|
year => $year,
|
|
|
|
|
have_review => $with_review,
|
|
|
|
|
statistics => $stats
|
2019-04-07 16:44:33 +00:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2019-03-22 15:56:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 20:20:59 +00:00
|
|
|
|
sub monthly_history {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $year = $self->stash('year');
|
|
|
|
|
my $month = $self->stash('month');
|
|
|
|
|
my @journeys;
|
|
|
|
|
my @months
|
|
|
|
|
= (
|
|
|
|
|
qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember)
|
|
|
|
|
);
|
|
|
|
|
|
2019-05-10 23:35:57 +00:00
|
|
|
|
if (
|
|
|
|
|
not( $year =~ m{ ^ [0-9]{4} $ }x
|
|
|
|
|
and $year > 1990
|
|
|
|
|
and $year < 2100
|
|
|
|
|
and $month =~ m{ ^ [0-9]{1,2} $ }x
|
|
|
|
|
and $month > 0
|
|
|
|
|
and $month < 13 )
|
|
|
|
|
)
|
2019-03-27 20:20:59 +00:00
|
|
|
|
{
|
2022-12-26 14:35:33 +00:00
|
|
|
|
$self->render('not_found');
|
|
|
|
|
return;
|
2019-03-27 20:20:59 +00:00
|
|
|
|
}
|
2022-12-26 14:35:33 +00:00
|
|
|
|
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->journeys->get(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
after => $interval_start,
|
|
|
|
|
before => $interval_end,
|
|
|
|
|
with_datetime => 1
|
|
|
|
|
);
|
2022-12-26 15:50:39 +00:00
|
|
|
|
|
|
|
|
|
if ( not @journeys ) {
|
|
|
|
|
$self->render( 'not_found',
|
|
|
|
|
message => 'Keine Zugfahrten im angefragten Monat gefunden.' );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 10:07:16 +00:00
|
|
|
|
my $stats = $self->journeys->get_stats(
|
2022-12-26 14:35:33 +00:00
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
year => $year,
|
|
|
|
|
month => $month
|
|
|
|
|
);
|
2019-03-27 20:20:59 +00:00
|
|
|
|
|
|
|
|
|
$self->respond_to(
|
|
|
|
|
json => {
|
|
|
|
|
json => {
|
|
|
|
|
journeys => [@journeys],
|
|
|
|
|
statistics => $stats
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
any => {
|
2019-04-07 16:44:33 +00:00
|
|
|
|
template => 'history_by_month',
|
2019-03-27 20:20:59 +00:00
|
|
|
|
journeys => [@journeys],
|
|
|
|
|
year => $year,
|
2019-03-27 20:46:52 +00:00
|
|
|
|
month => $month,
|
|
|
|
|
month_name => $months[ $month - 1 ],
|
2019-03-27 20:20:59 +00:00
|
|
|
|
statistics => $stats
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 15:56:49 +00:00
|
|
|
|
sub journey_details {
|
|
|
|
|
my ($self) = @_;
|
2019-04-23 16:08:07 +00:00
|
|
|
|
my $journey_id = $self->stash('id');
|
|
|
|
|
|
2023-02-15 19:01:43 +00:00
|
|
|
|
my $user = $self->current_user;
|
|
|
|
|
my $uid = $user->{id};
|
2019-03-22 15:56:49 +00:00
|
|
|
|
|
2019-04-23 16:08:07 +00:00
|
|
|
|
$self->param( journey_id => $journey_id );
|
2019-04-07 18:20:37 +00:00
|
|
|
|
|
2020-01-19 18:15:53 +00:00
|
|
|
|
if ( not( $journey_id and $journey_id =~ m{ ^ \d+ $ }x ) ) {
|
2019-03-22 15:56:49 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
2020-01-19 18:21:14 +00:00
|
|
|
|
status => 404,
|
2019-03-22 15:56:49 +00:00
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my $journey = $self->journeys->get_single(
|
2023-02-27 21:14:54 +00:00
|
|
|
|
uid => $uid,
|
|
|
|
|
journey_id => $journey_id,
|
|
|
|
|
verbose => 1,
|
|
|
|
|
with_datetime => 1,
|
|
|
|
|
with_polyline => 1,
|
|
|
|
|
with_visibility => 1,
|
2019-03-22 15:56:49 +00:00
|
|
|
|
);
|
2019-04-23 16:08:07 +00:00
|
|
|
|
|
|
|
|
|
if ($journey) {
|
2020-02-23 18:04:05 +00:00
|
|
|
|
my $map_data = $self->journeys_to_map_data(
|
|
|
|
|
journeys => [$journey],
|
|
|
|
|
include_manual => 1,
|
|
|
|
|
);
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $with_share;
|
2023-02-15 19:01:43 +00:00
|
|
|
|
my $share_text;
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $visibility
|
|
|
|
|
= $self->compute_effective_visibility(
|
|
|
|
|
$user->{default_visibility_str},
|
|
|
|
|
$journey->{visibility_str} );
|
|
|
|
|
|
|
|
|
|
if ( $visibility eq 'public'
|
|
|
|
|
or $visibility eq 'travelynx'
|
|
|
|
|
or $visibility eq 'unlisted' )
|
|
|
|
|
{
|
2023-02-15 19:01:43 +00:00
|
|
|
|
my $delay = 'pünktlich ';
|
|
|
|
|
if ( $journey->{rt_arrival} != $journey->{sched_arrival} ) {
|
|
|
|
|
$delay = sprintf(
|
|
|
|
|
'mit %+d ',
|
|
|
|
|
(
|
|
|
|
|
$journey->{rt_arrival}->epoch
|
|
|
|
|
- $journey->{sched_arrival}->epoch
|
|
|
|
|
) / 60
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-02-27 21:14:54 +00:00
|
|
|
|
$with_share = 1;
|
2023-02-15 19:01:43 +00:00
|
|
|
|
$share_text
|
|
|
|
|
= $journey->{km_route}
|
|
|
|
|
? sprintf( '%.0f km', $journey->{km_route} )
|
|
|
|
|
: 'Fahrt';
|
|
|
|
|
$share_text .= sprintf( ' mit %s %s – Ankunft %sum %s',
|
|
|
|
|
$journey->{type}, $journey->{no},
|
|
|
|
|
$delay, $journey->{rt_arrival}->strftime('%H:%M') );
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 16:08:07 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
2023-02-27 21:14:54 +00:00
|
|
|
|
error => undef,
|
|
|
|
|
journey => $journey,
|
|
|
|
|
journey_visibility => $visibility,
|
|
|
|
|
with_map => 1,
|
|
|
|
|
with_share => $with_share,
|
|
|
|
|
share_text => $share_text,
|
2020-02-23 18:04:05 +00:00
|
|
|
|
%{$map_data},
|
2019-04-23 16:08:07 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-03-22 15:56:49 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'journey',
|
2020-01-19 18:21:14 +00:00
|
|
|
|
status => 404,
|
2019-03-22 15:56:49 +00:00
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 21:14:54 +00:00
|
|
|
|
sub visibility_form {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $dep_ts = $self->param('dep_ts');
|
|
|
|
|
my $journey_id = $self->param('id');
|
|
|
|
|
my $action = $self->param('action') // 'none';
|
|
|
|
|
my $user = $self->current_user;
|
|
|
|
|
my $user_level = $user->{default_visibility_str};
|
|
|
|
|
my $uid = $user->{id};
|
|
|
|
|
my $status = $self->get_user_status;
|
2023-03-02 20:20:59 +00:00
|
|
|
|
my $visibility = $status->{visibility_str};
|
2023-02-27 21:14:54 +00:00
|
|
|
|
my $journey;
|
|
|
|
|
|
|
|
|
|
if ($journey_id) {
|
|
|
|
|
$journey = $self->journeys->get_single(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
journey_id => $journey_id,
|
|
|
|
|
with_datetime => 1,
|
|
|
|
|
with_visibility => 1,
|
|
|
|
|
);
|
2023-03-02 20:20:59 +00:00
|
|
|
|
$visibility = $journey->{visibility_str};
|
2023-02-27 21:14:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $action eq 'save' ) {
|
|
|
|
|
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_visibility',
|
|
|
|
|
error => 'csrf',
|
|
|
|
|
user_level => $user_level,
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elsif ( $dep_ts and $dep_ts != $status->{sched_departure}->epoch ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_visibility',
|
|
|
|
|
error => 'old',
|
|
|
|
|
user_level => $user_level,
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if ($dep_ts) {
|
|
|
|
|
$self->in_transit->update_visibility(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
visibility => $self->param('status_level'),
|
|
|
|
|
);
|
|
|
|
|
$self->redirect_to('/');
|
|
|
|
|
}
|
|
|
|
|
elsif ($journey_id) {
|
|
|
|
|
$self->journeys->update_visibility(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
id => $journey_id,
|
|
|
|
|
visibility => $self->param('status_level'),
|
|
|
|
|
);
|
|
|
|
|
$self->redirect_to( '/journey/' . $journey_id );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 20:20:59 +00:00
|
|
|
|
$self->param( status_level => $visibility );
|
2023-02-27 21:14:54 +00:00
|
|
|
|
|
|
|
|
|
if ($journey_id) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_visibility',
|
|
|
|
|
error => undef,
|
|
|
|
|
user_level => $user_level,
|
|
|
|
|
journey => $journey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elsif ( $status->{checked_in} ) {
|
|
|
|
|
$self->param( dep_ts => $status->{sched_departure}->epoch );
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_visibility',
|
|
|
|
|
error => undef,
|
|
|
|
|
user_level => $user_level,
|
|
|
|
|
journey => $status
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_visibility',
|
|
|
|
|
error => 'notfound',
|
|
|
|
|
user_level => $user_level,
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 20:40:51 +00:00
|
|
|
|
sub comment_form {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $dep_ts = $self->param('dep_ts');
|
|
|
|
|
my $status = $self->get_user_status;
|
|
|
|
|
|
|
|
|
|
if ( not $status->{checked_in} ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_comment',
|
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elsif ( not $dep_ts ) {
|
|
|
|
|
$self->param( dep_ts => $status->{sched_departure}->epoch );
|
|
|
|
|
$self->param( comment => $status->{comment} );
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_comment',
|
|
|
|
|
error => undef,
|
|
|
|
|
journey => $status
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elsif ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_comment',
|
|
|
|
|
error => undef,
|
|
|
|
|
journey => $status
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
elsif ( $dep_ts != $status->{sched_departure}->epoch ) {
|
|
|
|
|
|
|
|
|
|
# TODO find and update appropriate past journey (if it exists)
|
|
|
|
|
$self->param( comment => $status->{comment} );
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_comment',
|
|
|
|
|
error => undef,
|
|
|
|
|
journey => $status
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->app->log->debug("set comment");
|
2020-10-11 17:38:01 +00:00
|
|
|
|
$self->in_transit->update_user_data(
|
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
user_data => { comment => $self->param('comment') }
|
|
|
|
|
);
|
2019-12-06 20:40:51 +00:00
|
|
|
|
$self->redirect_to('/');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 18:20:37 +00:00
|
|
|
|
sub edit_journey {
|
2019-04-23 16:08:07 +00:00
|
|
|
|
my ($self) = @_;
|
|
|
|
|
my $journey_id = $self->param('journey_id');
|
|
|
|
|
my $uid = $self->current_user->{id};
|
2019-04-07 18:20:37 +00:00
|
|
|
|
|
2019-04-23 16:08:07 +00:00
|
|
|
|
if ( not( $journey_id =~ m{ ^ \d+ $ }x ) ) {
|
2019-04-07 18:20:37 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'edit_journey',
|
2020-01-19 18:21:14 +00:00
|
|
|
|
status => 404,
|
2019-04-07 18:20:37 +00:00
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my $journey = $self->journeys->get_single(
|
2019-11-24 19:29:52 +00:00
|
|
|
|
uid => $uid,
|
|
|
|
|
journey_id => $journey_id,
|
2019-12-08 18:46:45 +00:00
|
|
|
|
verbose => 1,
|
2019-11-24 19:29:52 +00:00
|
|
|
|
with_datetime => 1,
|
2019-04-07 18:20:37 +00:00
|
|
|
|
);
|
2019-04-08 19:20:04 +00:00
|
|
|
|
|
|
|
|
|
if ( not $journey ) {
|
2019-04-07 18:20:37 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'edit_journey',
|
2020-01-19 18:21:14 +00:00
|
|
|
|
status => 404,
|
2019-04-07 18:20:37 +00:00
|
|
|
|
error => 'notfound',
|
|
|
|
|
journey => {}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-08 19:20:04 +00:00
|
|
|
|
my $error = undef;
|
|
|
|
|
|
|
|
|
|
if ( $self->param('action') and $self->param('action') eq 'save' ) {
|
|
|
|
|
my $parser = DateTime::Format::Strptime->new(
|
|
|
|
|
pattern => '%d.%m.%Y %H:%M',
|
|
|
|
|
locale => 'de_DE',
|
|
|
|
|
time_zone => 'Europe/Berlin'
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-22 10:11:22 +00:00
|
|
|
|
my $db = $self->pg->db;
|
|
|
|
|
my $tx = $db->begin;
|
2019-04-08 19:20:04 +00:00
|
|
|
|
|
|
|
|
|
for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival))
|
|
|
|
|
{
|
|
|
|
|
my $datetime = $parser->parse_datetime( $self->param($key) );
|
|
|
|
|
if ( $datetime and $datetime->epoch ne $journey->{$key}->epoch ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->update(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
id => $journey->{id},
|
|
|
|
|
$key => $datetime
|
|
|
|
|
);
|
2019-04-08 19:20:04 +00:00
|
|
|
|
if ($error) {
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-01 15:15:20 +00:00
|
|
|
|
for my $key (qw(from_name to_name)) {
|
|
|
|
|
if ( defined $self->param($key)
|
|
|
|
|
and $self->param($key) ne $journey->{$key} )
|
|
|
|
|
{
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->update(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
id => $journey->{id},
|
|
|
|
|
$key => $self->param($key)
|
|
|
|
|
);
|
2020-02-01 15:15:20 +00:00
|
|
|
|
if ($error) {
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-23 10:02:22 +00:00
|
|
|
|
for my $key (qw(comment)) {
|
|
|
|
|
if (
|
|
|
|
|
defined $self->param($key)
|
|
|
|
|
and ( not $journey->{user_data}
|
|
|
|
|
or $journey->{user_data}{$key} ne $self->param($key) )
|
|
|
|
|
)
|
|
|
|
|
{
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->update(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
id => $journey->{id},
|
|
|
|
|
$key => $self->param($key)
|
|
|
|
|
);
|
2019-08-23 10:02:22 +00:00
|
|
|
|
if ($error) {
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-20 16:41:21 +00:00
|
|
|
|
if ( defined $self->param('route') ) {
|
|
|
|
|
my @route_old = map { $_->[0] } @{ $journey->{route} };
|
|
|
|
|
my @route_new = split( qr{\r?\n\r?}, $self->param('route') );
|
|
|
|
|
@route_new = grep { $_ ne '' } @route_new;
|
|
|
|
|
if ( join( '|', @route_old ) ne join( '|', @route_new ) ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->update(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
id => $journey->{id},
|
|
|
|
|
route => [@route_new]
|
|
|
|
|
);
|
2019-11-20 16:41:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-08 18:46:45 +00:00
|
|
|
|
{
|
2020-02-01 15:15:20 +00:00
|
|
|
|
my $cancelled_old = $journey->{cancelled} // 0;
|
2019-12-08 18:46:45 +00:00
|
|
|
|
my $cancelled_new = $self->param('cancelled') // 0;
|
|
|
|
|
if ( $cancelled_old != $cancelled_new ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->update(
|
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
id => $journey->{id},
|
|
|
|
|
cancelled => $cancelled_new
|
|
|
|
|
);
|
2019-12-08 18:46:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-08 19:20:04 +00:00
|
|
|
|
|
2019-04-22 10:11:22 +00:00
|
|
|
|
if ( not $error ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$journey = $self->journeys->get_single(
|
2019-11-24 19:29:52 +00:00
|
|
|
|
uid => $uid,
|
|
|
|
|
db => $db,
|
|
|
|
|
journey_id => $journey_id,
|
|
|
|
|
verbose => 1,
|
|
|
|
|
with_datetime => 1,
|
2019-04-08 19:20:04 +00:00
|
|
|
|
);
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->sanity_check($journey);
|
2019-04-08 19:20:04 +00:00
|
|
|
|
}
|
2019-04-22 10:11:22 +00:00
|
|
|
|
if ( not $error ) {
|
|
|
|
|
$tx->commit;
|
2019-04-23 16:08:07 +00:00
|
|
|
|
$self->redirect_to("/journey/${journey_id}");
|
2019-04-22 10:11:22 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-08 19:20:04 +00:00
|
|
|
|
}
|
2019-04-07 18:20:37 +00:00
|
|
|
|
|
|
|
|
|
for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) {
|
|
|
|
|
if ( $journey->{$key} and $journey->{$key}->epoch ) {
|
|
|
|
|
$self->param(
|
|
|
|
|
$key => $journey->{$key}->strftime('%d.%m.%Y %H:%M') );
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-20 16:41:21 +00:00
|
|
|
|
|
|
|
|
|
$self->param(
|
|
|
|
|
route => join( "\n", map { $_->[0] } @{ $journey->{route} } ) );
|
|
|
|
|
|
2020-02-01 15:15:20 +00:00
|
|
|
|
$self->param( cancelled => $journey->{cancelled} ? 1 : 0 );
|
|
|
|
|
$self->param( from_name => $journey->{from_name} );
|
|
|
|
|
$self->param( to_name => $journey->{to_name} );
|
2019-12-08 18:46:45 +00:00
|
|
|
|
|
2019-08-23 10:02:22 +00:00
|
|
|
|
for my $key (qw(comment)) {
|
|
|
|
|
if ( $journey->{user_data} and $journey->{user_data}{$key} ) {
|
|
|
|
|
$self->param( $key => $journey->{user_data}{$key} );
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-07 18:20:37 +00:00
|
|
|
|
|
|
|
|
|
$self->render(
|
|
|
|
|
'edit_journey',
|
2020-02-01 15:15:20 +00:00
|
|
|
|
with_autocomplete => 1,
|
|
|
|
|
error => $error,
|
|
|
|
|
journey => $journey
|
2019-04-07 18:20:37 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub add_journey_form {
|
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
2019-04-13 08:43:05 +00:00
|
|
|
|
if ( $self->param('action') and $self->param('action') eq 'save' ) {
|
|
|
|
|
my $parser = DateTime::Format::Strptime->new(
|
|
|
|
|
pattern => '%d.%m.%Y %H:%M',
|
|
|
|
|
locale => 'de_DE',
|
|
|
|
|
time_zone => 'Europe/Berlin'
|
|
|
|
|
);
|
|
|
|
|
my %opt;
|
|
|
|
|
|
|
|
|
|
my @parts = split( qr{\s+}, $self->param('train') );
|
|
|
|
|
|
|
|
|
|
if ( @parts == 2 ) {
|
|
|
|
|
@opt{ 'train_type', 'train_no' } = @parts;
|
|
|
|
|
}
|
|
|
|
|
elsif ( @parts == 3 ) {
|
|
|
|
|
@opt{ 'train_type', 'train_line', 'train_no' } = @parts;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$self->render(
|
|
|
|
|
'add_journey',
|
|
|
|
|
with_autocomplete => 1,
|
2022-02-20 20:45:51 +00:00
|
|
|
|
error =>
|
2019-04-13 08:43:05 +00:00
|
|
|
|
'Zug muss als „Typ Nummer“ oder „Typ Linie Nummer“ eingegeben werden.'
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival))
|
|
|
|
|
{
|
2019-04-26 17:53:01 +00:00
|
|
|
|
if ( $self->param($key) ) {
|
|
|
|
|
my $datetime = $parser->parse_datetime( $self->param($key) );
|
|
|
|
|
if ( not $datetime ) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'add_journey',
|
|
|
|
|
with_autocomplete => 1,
|
|
|
|
|
error => "${key}: Ungültiges Datums-/Zeitformat"
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$opt{$key} = $datetime;
|
2019-04-13 08:43:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:00:24 +00:00
|
|
|
|
$opt{rt_departure} //= $opt{sched_departure};
|
|
|
|
|
$opt{rt_arrival} //= $opt{sched_arrival};
|
|
|
|
|
|
2019-11-19 18:46:21 +00:00
|
|
|
|
for my $key (qw(dep_station arr_station route cancelled comment)) {
|
2019-04-13 08:43:05 +00:00
|
|
|
|
$opt{$key} = $self->param($key);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-19 18:46:21 +00:00
|
|
|
|
if ( $opt{route} ) {
|
|
|
|
|
$opt{route} = [ split( qr{\r?\n\r?}, $opt{route} ) ];
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 17:53:01 +00:00
|
|
|
|
my $db = $self->pg->db;
|
|
|
|
|
my $tx = $db->begin;
|
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$opt{db} = $db;
|
|
|
|
|
$opt{uid} = $self->current_user->{id};
|
2019-04-26 17:53:01 +00:00
|
|
|
|
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my ( $journey_id, $error ) = $self->journeys->add(%opt);
|
2019-04-13 08:43:05 +00:00
|
|
|
|
|
2019-04-26 17:53:01 +00:00
|
|
|
|
if ( not $error ) {
|
2020-08-16 19:20:23 +00:00
|
|
|
|
my $journey = $self->journeys->get_single(
|
2019-04-26 17:53:01 +00:00
|
|
|
|
uid => $self->current_user->{id},
|
|
|
|
|
db => $db,
|
|
|
|
|
journey_id => $journey_id,
|
|
|
|
|
verbose => 1
|
|
|
|
|
);
|
2020-08-16 19:20:23 +00:00
|
|
|
|
$error = $self->journeys->sanity_check($journey);
|
2019-04-26 17:53:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($error) {
|
|
|
|
|
$self->render(
|
|
|
|
|
'add_journey',
|
|
|
|
|
with_autocomplete => 1,
|
|
|
|
|
error => $error,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$tx->commit;
|
|
|
|
|
$self->redirect_to("/journey/${journey_id}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-04-13 08:43:05 +00:00
|
|
|
|
$self->render(
|
|
|
|
|
'add_journey',
|
|
|
|
|
with_autocomplete => 1,
|
2019-04-26 17:53:01 +00:00
|
|
|
|
error => undef
|
2019-04-13 08:43:05 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
2019-04-07 18:20:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 21:12:45 +00:00
|
|
|
|
1;
|