diff options
| author | Daniel Friesel <derf@finalrewind.org> | 2019-04-26 19:53:01 +0200 | 
|---|---|---|
| committer | Daniel Friesel <derf@finalrewind.org> | 2019-04-26 19:53:01 +0200 | 
| commit | e07063c52c51569b5f252a202f5d71b5f70a73e7 (patch) | |
| tree | 301f64f3fe95038a2551a81c927ff0206df93243 | |
| parent | 2cae5a51d2f2a8002864860f452fa9474dc2ecb3 (diff) | |
Add manual journey entry
Closes #3
| -rwxr-xr-x | lib/Travelynx.pm | 132 | ||||
| -rwxr-xr-x | lib/Travelynx/Controller/Traveling.pm | 65 | ||||
| -rw-r--r-- | templates/_history_trains.html.ep | 2 | ||||
| -rw-r--r-- | templates/add_journey.html.ep | 40 | ||||
| -rw-r--r-- | templates/cancelled.html.ep | 2 | ||||
| -rw-r--r-- | templates/history.html.ep | 34 | ||||
| -rw-r--r-- | templates/history_by_month.html.ep | 4 | ||||
| -rw-r--r-- | templates/history_by_year.html.ep | 4 | ||||
| -rw-r--r-- | templates/journey.html.ep | 31 | ||||
| -rw-r--r-- | templates/landingpage.html.ep | 2 | 
10 files changed, 197 insertions, 119 deletions
| diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 569b65f..017a2d4 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -220,27 +220,16 @@ sub startup {  		}  	); -	# Returns (checkin id, checkout id, error) +	# Returns (journey id, error)  	# Must be called during a transaction.  	# Must perform a rollback on error.  	$self->helper(  		'add_journey' => sub {  			my ( $self, %opt ) = @_; -			$self->app->log->error( -				"add_journey is not implemented at the moment"); -			return ( undef, undef, 'not implemented' ); - -			my $user_status = $self->get_user_status; -			if ( $user_status->{checked_in} or $user_status->{cancelled} ) { - -            # TODO: change database schema to one row per journey instead of two -				return ( undef, undef, -'Während einer Zugfahrt können momentan keine manuellen Einträge vorgenommen werden. Klingt komisch, ist aber so.' -				); -			} - +			my $db          = $opt{db};  			my $uid         = $self->current_user->{id}; +			my $now         = DateTime->now( time_zone => 'Europe/Berlin' );  			my $dep_station = get_station( $opt{dep_station} );  			my $arr_station = get_station( $opt{arr_station} ); @@ -251,66 +240,48 @@ sub startup {  				return ( undef, undef, 'Unbekannter Zielbahnhof' );  			} -			my $checkin_id; -			my $checkout_id; - -			eval { -				$checkin_id = $self->pg->db->insert( -					'user_actions', -					{ -						user_id    => $uid, -						action_id  => 'checkin', -						station_id => $self->get_station_id( -							ds100 => $dep_station->[0], -							name  => $dep_station->[1], -						), -						action_time => -						  DateTime->now( time_zone => 'Europe/Berlin' ), -						edited     => 0x0f, -						train_type => $opt{train_type}, -						train_line => $opt{train_line}, -						train_no   => $opt{train_no}, -						sched_time => $opt{sched_departure}, -						real_time  => $opt{rt_departure}, -					}, -					{ returning => 'id' } -				)->hash->{id}; +			my $entry = { +				user_id            => $uid, +				train_type         => $opt{train_type}, +				train_line         => $opt{train_line}, +				train_no           => $opt{train_no}, +				train_id           => 'manual', +				checkin_station_id => $self->get_station_id( +					ds100 => $dep_station->[0], +					name  => $dep_station->[1], +				), +				checkin_time        => $now, +				sched_departure     => $opt{sched_departure}, +				real_departure      => $opt{rt_departure}, +				checkout_station_id => $self->get_station_id( +					ds100 => $arr_station->[0], +					name  => $arr_station->[1], +				), +				sched_arrival => $opt{sched_arrival}, +				real_arrival  => $opt{rt_arrival}, +				checkout_time => $now, +				edited        => 0x3fff, +				cancelled     => $opt{cancelled} ? 1 : 0, +				route         => $dep_station->[1] . '|' . $arr_station->[1],  			}; -			if ($@) { -				$self->app->log->error( -					"add_journey($uid, checkin): INSERT failed: $@"); -				return ( undef, undef, 'INSERT failed: ' . $@ ); +			if ( $opt{comment} ) { +				$entry->{messages} = '0:' . $opt{comment};  			} +			my $journey_id = undef;  			eval { -				$checkout_id = $self->pg->db->insert( -					'user_actions', -					{ -						user_id    => $uid, -						action_id  => 'checkout', -						station_id => $self->get_station_id( -							ds100 => $arr_station->[0], -							name  => $arr_station->[1], -						), -						action_time => -						  DateTime->now( time_zone => 'Europe/Berlin' ), -						edited     => 0x0f, -						train_type => $opt{train_type}, -						train_line => $opt{train_line}, -						train_no   => $opt{train_no}, -						sched_time => $opt{sched_arrival}, -						real_time  => $opt{rt_arrival}, -					}, -					{ returnning => 'id' } -				)->hash->{id}; +				$journey_id +				  = $db->insert( 'journeys', $entry, { returning => 'id' } ) +				  ->hash->{id};  			}; +  			if ($@) { -				$self->app->log->error( -					"add_journey($uid, checkout): INSERT failed: $@"); -				return ( undef, undef, 'INSERT failed: ' . $@ ); +				$self->app->log->error("add_journey($uid): $@"); +				return ( undef, 'add_journey failed: ' . $@ );  			} -			return ( $checkin_id, $checkout_id, undef ); + +			return ( $journey_id, undef );  		}  	); @@ -1097,6 +1068,31 @@ sub startup {  	);  	$self->helper( +		'get_oldest_journey_ts' => sub { +			my ($self) = @_; + +			my $res_h = $self->pg->db->select( +				'journeys_str', +				['sched_dep_ts'], +				{ +					user_id => $self->current_user->{id}, +				}, +				{ +					limit    => 1, +					order_by => { +						-asc => 'real_dep_ts', +					}, +				} +			)->hash; + +			if ($res_h) { +				return epoch_to_dt( $res_h->{sched_dep_ts} ); +			} +			return undef; +		} +	); + +	$self->helper(  		'get_user_travels' => sub {  			my ( $self, %opt ) = @_; @@ -1171,12 +1167,12 @@ sub startup {  					}  					$ref->{messages} = [ reverse @parsed_messages ];  					$ref->{sched_duration} -					  = $ref->{sched_arrival} +					  = $ref->{sched_arrival}->epoch  					  ? $ref->{sched_arrival}->epoch  					  - $ref->{sched_departure}->epoch  					  : undef;  					$ref->{rt_duration} -					  = $ref->{rt_arrival} +					  = $ref->{rt_arrival}->epoch  					  ? $ref->{rt_arrival}->epoch - $ref->{rt_departure}->epoch  					  : undef;  					my ( $km, $skip ) diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index 5282d9a..ee8d27d 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -141,7 +141,7 @@ sub log_action {  	}  	elsif ( $params->{action} eq 'undo' ) {  		my $status = $self->get_user_status; -		my $error = $self->undo( $params->{undo_id} ); +		my $error  = $self->undo( $params->{undo_id} );  		if ($error) {  			$self->render(  				json => { @@ -152,7 +152,7 @@ sub log_action {  		}  		else {  			my $redir = '/'; -			if ($status->{checked_in} or $status->{cancelled}) { +			if ( $status->{checked_in} or $status->{cancelled} ) {  				$redir = '/s/' . $status->{dep_ds100};  			}  			$self->render( @@ -562,37 +562,60 @@ sub add_journey_form {  		for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival))  		{ -			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; +			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;  			} -			$opt{$key} = $datetime;  		} -		for my $key (qw(dep_station arr_station)) { +		for my $key (qw(dep_station arr_station cancelled comment)) {  			$opt{$key} = $self->param($key);  		} -		#my ( $checkin_id, $checkout_id, $error ) = $self->add_journey(%opt); +		my $db = $self->pg->db; +		my $tx = $db->begin; + +		$opt{db} = $db; + +		my ( $journey_id, $error ) = $self->add_journey(%opt); +		if ( not $error ) { +			my $journey = $self->get_journey( +				uid        => $self->current_user->{id}, +				db         => $db, +				journey_id => $journey_id, +				verbose    => 1 +			); +			$error = $self->journey_sanity_check($journey); +		} + +		if ($error) { +			$self->render( +				'add_journey', +				with_autocomplete => 1, +				error             => $error, +			); +		} +		else { +			$tx->commit; +			$self->redirect_to("/journey/${journey_id}"); +		} +	} +	else {  		$self->render(  			'add_journey',  			with_autocomplete => 1, -			error             => 'not implemented', +			error             => undef  		); -		return;  	} - -	$self->render( -		'add_journey', -		with_autocomplete => 1, -		error             => undef -	);  }  1; diff --git a/templates/_history_trains.html.ep b/templates/_history_trains.html.ep index 2328285..d422165 100644 --- a/templates/_history_trains.html.ep +++ b/templates/_history_trains.html.ep @@ -13,7 +13,7 @@  				% for my $travel (@{$journeys}) {  					% my $detail_link = '/journey/' . $travel->{id};  					<tr> -						<td><%= $travel->{sched_departure}->strftime('%d.%m.') %></td> +						<td><%= $travel->{sched_departure}->strftime($date_format) %></td>  						<td><a href="<%= $detail_link %>"><%= $travel->{type} %> <%= $travel->{line} // $travel->{no} %></a></td>  						<td>  						<a href="<%= $detail_link %>" class="unmarked"> diff --git a/templates/add_journey.html.ep b/templates/add_journey.html.ep index e07f78f..10e74eb 100644 --- a/templates/add_journey.html.ep +++ b/templates/add_journey.html.ep @@ -1,4 +1,18 @@  <h1>Zugfahrt eingeben</h1> +% if (not get_oldest_journey_ts()) { +	<div class="row"> +		<div class="col s12"> +			<div class="card yellow lighten-4"> +				<div class="card-content"> +					<span class="card-title">Hinweis</span> +					<p>travelynx ist darauf ausgelegt, über die Hauptseite in +						Echtzeit in Züge ein- und auszuchecken. Die manuelle +						Eingabe von Zugfahrten ist nur als Notlösung vorgesehen.</p> +				</div> +			</div> +		</div> +	</div> +% }  % if ($error) {  	<div class="row">  		<div class="col s12"> @@ -23,37 +37,49 @@  %= form_for '/journey/add' => (method => 'POST') => begin  	%= csrf_field  	<div class="row"> -		<div class="input-field col s12"> +		<div class="input-field col s12 m6 l6">  			%= text_field 'train', id => 'train', class => 'validate', required => undef, pattern => '[0-9a-zA-Z]+ +[0-9a-zA-Z]* *[0-9]+'  			<label for="train">Zug (Typ Linie Nummer)</label>  		</div> +		<div class="input-field col s12 m6 l6"> +			<label> +				%= check_box cancelled => 1 +				<span>Fahrt ist ausgefallen</span> +			</label> +		</div>  	</div>  	<div class="row">  		<div class="input-field col s12">  			%= text_field 'dep_station', id => 'dep_station', class => 'autocomplete validate', required => undef -			<label for="dep_station">Startbahnhof (Name oder DS100)</label> +			<label for="dep_station">Start (Name oder DS100)</label>  		</div>  		<div class="input-field col s12">  			%= text_field 'sched_departure', id => 'sched_departure', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]'  			<label for="sched_departure">Geplante Abfahrt</label>  		</div>  		<div class="input-field col s12"> -			%= text_field 'rt_departure', id => 'rt_departure', class => 'validate', pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' -			<label for="rt_departure">Tatsächliche Abfahrt (optional)</label> +			%= text_field 'rt_departure', id => 'rt_departure', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' +			<label for="rt_departure">Tatsächliche Abfahrt</label>  		</div>  	</div>  	<div class="row">  		<div class="input-field col s12">  			%= text_field 'arr_station', id => 'arr_station', class => 'autocomplete validate', required => undef -			<label for="arr_station">Zielbahnhof (Name oder DS100)</label> +			<label for="arr_station">Ziel (Name oder DS100)</label>  		</div>  		<div class="input-field col s12">  			%= text_field 'sched_arrival', id => 'sched_arrival', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]'  			<label for="sched_arrival">Geplante Ankunft</label>  		</div>  		<div class="input-field col s12"> -			%= text_field 'rt_arrival', id => 'rt_arrival', class => 'validate', pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' -			<label for="rt_arrival">Tatsächliche Ankunft (optional)</label> +			%= text_field 'rt_arrival', id => 'rt_arrival', class => 'validate', required => undef, pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' +			<label for="rt_arrival">Tatsächliche Ankunft</label> +		</div> +	</div> +	<div class="row"> +		<div class="input-field col s12"> +			%= text_field 'comment' +			<label for="comment">Kommentar</label>  		</div>  	</div>  	<div class="row"> diff --git a/templates/cancelled.html.ep b/templates/cancelled.html.ep index 88380ce..4fab434 100644 --- a/templates/cancelled.html.ep +++ b/templates/cancelled.html.ep @@ -17,4 +17,4 @@  	</div>  </div> -%= include '_history_trains', journeys => stash('journeys'); +%= include '_history_trains', date_format => '%d.%m.%Y', journeys => stash('journeys'); diff --git a/templates/history.html.ep b/templates/history.html.ep index 05f4777..246d6af 100644 --- a/templates/history.html.ep +++ b/templates/history.html.ep @@ -8,24 +8,44 @@  </div>  <div class="row"> -	<div class="col s12 m12 l4 center-align"> +	<div class="col s12 m12 l3 center-align">  		<a href="/cancelled" class="waves-effect waves-light btn"><i class="material-icons left">cancel</i> Zugausfälle</a>  	</div> -	<div class="col s12 m12 l4"> -	  -	</div> +	<div class="col s12 m12 l1"> </div>  	<div class="col s12 m12 l4 center-align"> +		<a href="/journey/add" class="waves-effect waves-light btn"><i class="material-icons left">add</i> Neue Fahrt</a> +	</div> +	<div class="col s12 m12 l1"> </div> +	<div class="col s12 m12 l3 center-align">  		<a href="/history.json" class="waves-effect waves-light btn"><i class="material-icons left">cloud</i> JSON-Export</a>  	</div>  </div>  <h2>Nach Jahr</h2> -% my $since = get_user_data()->{registered_at}; +% my $since = get_oldest_journey_ts();  % my $now = DateTime->now(time_zone => 'Europe/Berlin'); -%= include '_history_years', current => '', since => $since->clone, now => $now; +% if ($since) { +	%= include '_history_years', current => '', since => $since->clone, now => $now; +% } +% else { +	<div class="row"> +		<div class="col s12"> +			Noch keine Fahrten. +		</div> +	</div> +% }  <h2>Nach Monat</h2> -%= include '_history_months', current => '', since => $since->clone, now => $now; +% if ($since) { +	%= include '_history_months', current => '', since => $since->clone, now => $now; +% } +% else { +	<div class="row"> +		<div class="col s12"> +			Noch keine Fahrten. +		</div> +	</div> +% }  % if (stash('statistics')) {  	%= include '_history_stats', stats => stash('statistics'); diff --git a/templates/history_by_month.html.ep b/templates/history_by_month.html.ep index 3794858..64d42c6 100644 --- a/templates/history_by_month.html.ep +++ b/templates/history_by_month.html.ep @@ -1,4 +1,4 @@ -% my $since = get_user_data()->{registered_at}; +% my $since = get_oldest_journey_ts();  % my $now = DateTime->now(time_zone => 'Europe/Berlin');  %= include '_history_months', current => "${year}/${month}", since => $since, now => $now; @@ -9,5 +9,5 @@  % }  % if (stash('journeys')) { -	%= include '_history_trains', journeys => stash('journeys'); +	%= include '_history_trains', date_format => '%d.%m.', journeys => stash('journeys');  % } diff --git a/templates/history_by_year.html.ep b/templates/history_by_year.html.ep index 97c223f..f73d7ec 100644 --- a/templates/history_by_year.html.ep +++ b/templates/history_by_year.html.ep @@ -1,4 +1,4 @@ -% my $since = get_user_data()->{registered_at}; +% my $since = get_oldest_journey_ts();  % my $now = DateTime->now(time_zone => 'Europe/Berlin');  %= include '_history_years', current => $year, since => $since, now => $now; @@ -9,5 +9,5 @@  % }  % if (stash('journeys')) { -	%= include '_history_trains', journeys => stash('journeys'); +	%= include '_history_trains', date_format => '%d.%m.', journeys => stash('journeys');  % } diff --git a/templates/journey.html.ep b/templates/journey.html.ep index 4af9694..4d0f3fd 100644 --- a/templates/journey.html.ep +++ b/templates/journey.html.ep @@ -121,15 +121,27 @@  						% }  					</td>  				</tr> -				<tr> -					<th scope="row">Meldungen</th> -					<td> -						% for my $message (@{$journey->{messages} // []}) { -							% my ($ts, $msg) = @{$message}; -							<%= $ts->strftime('%d.%m.%Y %H:%M') %> : <%= $msg %><br/> -						% } -					</td> -				</tr> +				% if ($journey->{edited} == 0x3fff) { +					<tr> +						<th scope="row">Kommentar</th> +						<td> +							% for my $message (@{$journey->{messages} // []}) { +								<%= $message->[1] %><br/> +							% } +						</td> +					</tr> +				% } +				% else { +					<tr> +						<th scope="row">Meldungen</th> +						<td> +							% for my $message (@{$journey->{messages} // []}) { +								% my ($ts, $msg) = @{$message}; +								<%= $ts->strftime('%d.%m.%Y %H:%M') %> : <%= $msg %><br/> +							% } +						</td> +					</tr> +				% }  				<tr>  					<th scope="row">Route</th>  					<td> @@ -172,6 +184,7 @@  			%= form_for '/journey/edit' => (method => 'POST') => begin  				%= hidden_field 'journey_id' => param('journey_id')  				<button class="btn waves-effect waves-light" type="submit" name="action" value="edit"> +					<i class="material-icons left">edit</i>  					Bearbeiten  				</button>  			%= end diff --git a/templates/landingpage.html.ep b/templates/landingpage.html.ep index e694229..c6551ec 100644 --- a/templates/landingpage.html.ep +++ b/templates/landingpage.html.ep @@ -73,7 +73,7 @@  		</div>  	</div>  	<h1>Letzte Fahrten</h1> -	%= include '_history_trains', journeys => [get_user_travels(limit => 5)]; +	%= include '_history_trains', date_format => '%d.%m', journeys => [get_user_travels(limit => 5)];  % }  % else {  	<div class="row"> | 
