mirror of
https://github.com/derf/travelynx
synced 2024-11-10 15:04:17 +00:00
it's a secret to everybody.
This commit is contained in:
parent
73fa1a960c
commit
3abe6aed5c
5 changed files with 400 additions and 9 deletions
|
@ -2225,6 +2225,7 @@ sub startup {
|
|||
$authed_r->get('/history/commute')->to('traveling#commute');
|
||||
$authed_r->get('/history/map')->to('traveling#map_history');
|
||||
$authed_r->get('/history/:year')->to('traveling#yearly_history');
|
||||
$authed_r->get('/history/:year/review')->to('traveling#year_in_review');
|
||||
$authed_r->get('/history/:year/:month')->to('traveling#monthly_history');
|
||||
$authed_r->get('/journey/add')->to('traveling#add_journey_form');
|
||||
$authed_r->get('/journey/comment')->to('traveling#comment_form');
|
||||
|
|
|
@ -1448,11 +1448,73 @@ sub csv_history {
|
|||
);
|
||||
}
|
||||
|
||||
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',
|
||||
title => "travelynx Jahresrückblick $year",
|
||||
year => $year,
|
||||
stats => $stats,
|
||||
review => $review
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
sub yearly_history {
|
||||
my ($self) = @_;
|
||||
my $year = $self->stash('year');
|
||||
my @journeys;
|
||||
my $stats;
|
||||
|
||||
# DateTime is very slow when looking far into the future due to DST changes
|
||||
# -> Limit time range to avoid accidental DoS.
|
||||
|
@ -1484,11 +1546,17 @@ sub yearly_history {
|
|||
return;
|
||||
}
|
||||
|
||||
$stats = $self->journeys->get_stats(
|
||||
my $stats = $self->journeys->get_stats(
|
||||
uid => $self->current_user->{id},
|
||||
year => $year
|
||||
);
|
||||
|
||||
my $with_review;
|
||||
my $now = $self->now;
|
||||
if ( $year < $now->year or ( $now->month == 12 and $now->day == 31 ) ) {
|
||||
$with_review = 1;
|
||||
}
|
||||
|
||||
$self->respond_to(
|
||||
json => {
|
||||
json => {
|
||||
|
@ -1497,10 +1565,11 @@ sub yearly_history {
|
|||
}
|
||||
},
|
||||
any => {
|
||||
template => 'history_by_year',
|
||||
journeys => [@journeys],
|
||||
year => $year,
|
||||
statistics => $stats
|
||||
template => 'history_by_year',
|
||||
journeys => [@journeys],
|
||||
year => $year,
|
||||
have_review => $with_review,
|
||||
statistics => $stats
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1511,7 +1580,6 @@ sub monthly_history {
|
|||
my $year = $self->stash('year');
|
||||
my $month = $self->stash('month');
|
||||
my @journeys;
|
||||
my $stats;
|
||||
my @months
|
||||
= (
|
||||
qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember)
|
||||
|
@ -1552,7 +1620,7 @@ sub monthly_history {
|
|||
return;
|
||||
}
|
||||
|
||||
$stats = $self->journeys->get_stats(
|
||||
my $stats = $self->journeys->get_stats(
|
||||
uid => $self->current_user->{id},
|
||||
year => $year,
|
||||
month => $month
|
||||
|
|
|
@ -34,6 +34,53 @@ sub epoch_to_dt {
|
|||
);
|
||||
}
|
||||
|
||||
sub min_to_human {
|
||||
my ($minutes) = @_;
|
||||
|
||||
my @ret;
|
||||
|
||||
if ( $minutes >= 14 * 24 * 60 ) {
|
||||
push( @ret, int( $minutes / ( 7 * 24 * 60 ) ) . ' Wochen' );
|
||||
}
|
||||
elsif ( $minutes >= 7 * 24 * 60 ) {
|
||||
push( @ret, '1 Woche' );
|
||||
}
|
||||
$minutes %= 7 * 24 * 60;
|
||||
|
||||
if ( $minutes >= 2 * 24 * 60 ) {
|
||||
push( @ret, int( $minutes / ( 24 * 60 ) ) . ' Tage' );
|
||||
}
|
||||
elsif ( $minutes >= 24 * 60 ) {
|
||||
push( @ret, '1 Tag' );
|
||||
}
|
||||
$minutes %= 24 * 60;
|
||||
|
||||
if ( $minutes >= 2 * 60 ) {
|
||||
push( @ret, int( $minutes / 60 ) . ' Stunden' );
|
||||
}
|
||||
elsif ( $minutes >= 60 ) {
|
||||
push( @ret, '1 Stunde' );
|
||||
}
|
||||
$minutes %= 60;
|
||||
|
||||
if ( $minutes >= 2 ) {
|
||||
push( @ret, "$minutes Minuten" );
|
||||
}
|
||||
elsif ($minutes) {
|
||||
push( @ret, "1 Minute" );
|
||||
}
|
||||
|
||||
if ( @ret == 1 ) {
|
||||
return $ret[0];
|
||||
}
|
||||
|
||||
if ( @ret > 2 ) {
|
||||
my $last = pop(@ret);
|
||||
return join( ', ', @ret ) . " und $last";
|
||||
}
|
||||
return "$ret[0] und $ret[1]";
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ( $class, %opt ) = @_;
|
||||
|
||||
|
@ -991,6 +1038,165 @@ sub get_travel_distance {
|
|||
$distance_beeline, $skipped );
|
||||
}
|
||||
|
||||
sub compute_review {
|
||||
my ( $self, $stats, @journeys ) = @_;
|
||||
my $longest_km;
|
||||
my $longest_t;
|
||||
my $shortest_km;
|
||||
my $shortest_t;
|
||||
my $message_count
|
||||
; # anzahl fahrten bei denen irgendeine nachricht vermerkt war -> irgendwas war anders als geplant
|
||||
my %num_by_message; # für jede nachricht
|
||||
my %num_by_wrtype
|
||||
; # zugtyp, sofern wagenreihung verfügbar. 'none' für nicht verfügbar.
|
||||
my %num_by_linetype; # zugtyp nach "ICE 123" / "RE 127".
|
||||
my %num_by_stop; # arr/dep name
|
||||
|
||||
if ( not $stats or not @journeys or $stats->{num_trains} == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
my %review;
|
||||
|
||||
my $trains_per_journey = $stats->{num_trains} / $stats->{num_journeys};
|
||||
my $avg_change_count = sprintf( '%.1f', $trains_per_journey - 1 );
|
||||
my $min_total = $stats->{min_travel_real} + $stats->{min_interchange_real};
|
||||
|
||||
for my $journey (@journeys) {
|
||||
if ( $journey->{rt_duration} ) {
|
||||
if ( not $longest_t
|
||||
or $journey->{rt_duration} > $longest_t->{rt_duration} )
|
||||
{
|
||||
$longest_t = $journey;
|
||||
}
|
||||
if ( not $shortest_t
|
||||
or $journey->{rt_duration} < $shortest_t->{rt_duration} )
|
||||
{
|
||||
$shortest_t = $journey;
|
||||
}
|
||||
}
|
||||
if ( $journey->{km_route} ) {
|
||||
if ( not $longest_km
|
||||
or $journey->{km_route} > $longest_km->{km_route} )
|
||||
{
|
||||
$longest_km = $journey;
|
||||
}
|
||||
if ( not $shortest_km
|
||||
or $journey->{km_route} < $shortest_km->{km_route} )
|
||||
{
|
||||
$shortest_km = $journey;
|
||||
}
|
||||
}
|
||||
if ( $journey->{messages} and @{ $journey->{messages} } ) {
|
||||
$message_count += 1;
|
||||
for my $message ( @{ $journey->{messages} } ) {
|
||||
$num_by_message{ $message->[1] } += 1;
|
||||
}
|
||||
}
|
||||
if ( $journey->{type} ) {
|
||||
$num_by_linetype{ $journey->{type} } += 1;
|
||||
}
|
||||
if ( $journey->{from_name} ) {
|
||||
$num_by_stop{ $journey->{from_name} } += 1;
|
||||
}
|
||||
if ( $journey->{to_name} ) {
|
||||
$num_by_stop{ $journey->{to_name} } += 1;
|
||||
}
|
||||
}
|
||||
|
||||
my @linetypes = sort { $b->[1] <=> $a->[1] }
|
||||
map { [ $_, $num_by_linetype{$_} ] } keys %num_by_linetype;
|
||||
my @stops = sort { $b->[1] <=> $a->[1] }
|
||||
map { [ $_, $num_by_stop{$_} ] } keys %num_by_stop;
|
||||
|
||||
my @reasons = sort { $b->[1] <=> $a->[1] }
|
||||
map { [ $_, $num_by_message{$_} ] } keys %num_by_message;
|
||||
|
||||
$review{num_stops} = scalar @stops;
|
||||
$review{trains_per_day} = sprintf( '%.1f', $stats->{num_trains} / 365 );
|
||||
$review{km_route} = sprintf( '%.0f', $stats->{km_route} );
|
||||
$review{km_beeline} = sprintf( '%.0f', $stats->{km_beeline} );
|
||||
$review{km_circle} = sprintf( '%.1f', $stats->{km_route} / 40030 );
|
||||
$review{km_diag} = sprintf( '%.1f', $stats->{km_route} / 12742 );
|
||||
|
||||
$review{traveling_min_total} = $min_total;
|
||||
$review{traveling_percentage_year}
|
||||
= sprintf( "%.1f%%", $min_total * 100 / 525948.77 );
|
||||
$review{traveling_time_year} = min_to_human($min_total);
|
||||
|
||||
if (@linetypes) {
|
||||
$review{typical_type} = $linetypes[0][0];
|
||||
}
|
||||
if ( @stops >= 3 ) {
|
||||
my $desc = q{};
|
||||
$review{typical_stops_3} = [ $stops[0][0], $stops[1][0], $stops[2][0] ];
|
||||
}
|
||||
elsif ( @stops == 2 ) {
|
||||
$review{typical_stops_2} = [ $stops[0][0], $stops[1][0] ];
|
||||
}
|
||||
$review{typical_time}
|
||||
= min_to_human( $stats->{min_travel_real} / $stats->{num_trains} );
|
||||
$review{typical_km}
|
||||
= sprintf( '%.0f', $stats->{km_route} / $stats->{num_trains} );
|
||||
$review{typical_kmh} = sprintf( '%.0f',
|
||||
$stats->{km_route} / ( $stats->{min_travel_real} / 60 ) );
|
||||
$review{typical_delay_dep}
|
||||
= sprintf( '%.0f', $stats->{delay_dep} / $stats->{num_trains} );
|
||||
$review{typical_delay_dep_h} = min_to_human( $review{typical_delay_dep} );
|
||||
$review{typical_delay_arr}
|
||||
= sprintf( '%.0f', $stats->{delay_arr} / $stats->{num_trains} );
|
||||
$review{typical_delay_arr_h} = min_to_human( $review{typical_delay_arr} );
|
||||
|
||||
$review{longest_t_time} = min_to_human( $longest_t->{rt_duration} / 60 );
|
||||
$review{longest_t_type} = $longest_t->{type};
|
||||
$review{longest_t_lineno} = $longest_t->{line} // $longest_t->{no};
|
||||
$review{longest_t_from} = $longest_t->{from_name};
|
||||
$review{longest_t_to} = $longest_t->{to_name};
|
||||
$review{longest_t_id} = $longest_t->{id};
|
||||
|
||||
$review{longest_km_km} = sprintf( '%.0f', $longest_km->{km_route} );
|
||||
$review{longest_km_type} = $longest_km->{type};
|
||||
$review{longest_km_lineno} = $longest_km->{line} // $longest_km->{no};
|
||||
$review{longest_km_from} = $longest_km->{from_name};
|
||||
$review{longest_km_to} = $longest_km->{to_name};
|
||||
$review{longest_km_id} = $longest_km->{id};
|
||||
|
||||
$review{shortest_t_time} = min_to_human( $shortest_t->{rt_duration} / 60 );
|
||||
$review{shortest_t_type} = $shortest_t->{type};
|
||||
$review{shortest_t_lineno} = $shortest_t->{line} // $shortest_t->{no};
|
||||
$review{shortest_t_from} = $shortest_t->{from_name};
|
||||
$review{shortest_t_to} = $shortest_t->{to_name};
|
||||
$review{shortest_t_id} = $shortest_t->{id};
|
||||
|
||||
$review{shortest_km_m} = sprintf( '%.0f', $shortest_km->{km_route} * 1000 );
|
||||
$review{shortest_km_type} = $shortest_km->{type};
|
||||
$review{shortest_km_lineno} = $shortest_km->{line} // $shortest_km->{no};
|
||||
$review{shortest_km_from} = $shortest_km->{from_name};
|
||||
$review{shortest_km_to} = $shortest_km->{to_name};
|
||||
$review{shortest_km_id} = $shortest_km->{id};
|
||||
|
||||
$review{issue_percent}
|
||||
= sprintf( '%.0f%%', $message_count * 100 / $stats->{num_trains} );
|
||||
for my $i ( 0 .. 2 ) {
|
||||
if ( $reasons[$i] ) {
|
||||
my $p = 'issue' . ( $i + 1 );
|
||||
$review{"${p}_count"} = $reasons[$i][1];
|
||||
$review{"${p}_text"} = $reasons[$i][0];
|
||||
}
|
||||
}
|
||||
|
||||
printf( "In %.0f%% der Fahrten war irgendetwas nicht wie vorgesehen\n",
|
||||
$message_count * 100 / $stats->{num_trains} );
|
||||
say "Die drei häufigsten Anmerkungen waren:";
|
||||
for my $i ( 0 .. 2 ) {
|
||||
if ( $reasons[$i] ) {
|
||||
printf( "%d× %s\n", $reasons[$i][1], $reasons[$i][0] );
|
||||
}
|
||||
}
|
||||
|
||||
return \%review;
|
||||
}
|
||||
|
||||
sub compute_stats {
|
||||
my ( $self, @journeys ) = @_;
|
||||
my $km_route = 0;
|
||||
|
@ -1093,7 +1299,8 @@ sub get_stats {
|
|||
# checks out of a train or manually edits/adds a journey.
|
||||
|
||||
if (
|
||||
not $opt{write_only}
|
||||
not $opt{write_only}
|
||||
and not $opt{review}
|
||||
and my $stats = $self->stats_cache->get(
|
||||
uid => $uid,
|
||||
db => $db,
|
||||
|
@ -1148,6 +1355,10 @@ sub get_stats {
|
|||
stats => $stats
|
||||
);
|
||||
|
||||
if ( $opt{review} ) {
|
||||
return ( $stats, $self->compute_review( $stats, @journeys ) );
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
|
|
|
@ -266,4 +266,9 @@ $(document).ready(function() {
|
|||
$('a[href]').click(function() {
|
||||
$('nav .preloader-wrapper').addClass('active');
|
||||
});
|
||||
const elems = document.querySelectorAll('.carousel');
|
||||
const instances = M.Carousel.init(elems, {
|
||||
fullWidth: true,
|
||||
indicators: true}
|
||||
);
|
||||
});
|
||||
|
|
106
templates/year_in_review.html.ep
Normal file
106
templates/year_in_review.html.ep
Normal file
|
@ -0,0 +1,106 @@
|
|||
<div class="row">
|
||||
<div class="col s12 m12 l12">
|
||||
<div class="carousel carousel-slider center">
|
||||
<div class="carousel-item" href="#one">
|
||||
<h2>Jahresrückblick <%= $year %></h2>
|
||||
<p>
|
||||
Du hast in diesem Jahr <strong><%= $stats->{num_trains} %> Fahrten</strong> von und zu <strong><%= $review->{num_stops} %> Betriebsstellen</strong> in travelynx erfasst.
|
||||
% if ($stats->{num_trains} > 365) {
|
||||
Das sind mehr als <%= $review->{trains_per_day} %> Züge pro Tag!
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
% if ($review->{traveling_min_total} > 525) {
|
||||
Insgesamt hast du mindestens <strong><%= $review->{traveling_percentage_year} %> des Jahres</strong>
|
||||
(<%= $review->{traveling_time_year} %>) in Zügen und auf Bahnhöfen verbracht.
|
||||
% }
|
||||
% else {
|
||||
Insgesamt hast du mindestens <strong><%= $review->{traveling_time_year} %></strong> in Zügen und auf Bahnhöfen verbracht.
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
Dabei hast du ca. <strong><%= $review->{km_route} %> km</strong> (Luftlinie: <%= $review->{km_beeline} %> km) auf Schienen zurückgelegt.
|
||||
% if ($review->{km_circle} > 1) {
|
||||
Das entspricht <%= $review->{km_circle} %> Fahrten um die Erde.
|
||||
% }
|
||||
% elsif ($review->{km_diag} > 1) {
|
||||
Das entspricht <%= $review->{km_diag} %> Reisen zum Mittelpunkt der Erde und zurück.
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
<em>Hier streichen</em> 🐈 <em>oder unten klicken für nächste Seite</em>
|
||||
</p>
|
||||
</div>
|
||||
<div class="carousel-item" href="#two">
|
||||
<h2>Eine typische Zugfahrt</h2>
|
||||
<p>
|
||||
% if ($review->{typical_stops_3} and $review->{typical_type}) {
|
||||
… führte dich mit
|
||||
% if ($review->{typical_type} eq 'S') {
|
||||
einer <strong>S-Bahn</strong>
|
||||
% }
|
||||
% else {
|
||||
einem <strong><%= $review->{typical_type} %></strong>
|
||||
% }
|
||||
durch das Dreieck <%= join(' / ', @{$review->{typical_stops_3}}) %>.
|
||||
% }
|
||||
% elsif ($review->{typical_stops_2}) {
|
||||
… befand sich jederzeit auf deiner Pendelstrecke zwischen <strong><%= $review->{typical_stops_2}[0] %></strong> und <strong><%= $review->{typical_stops_2}[1] %></strong>.
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
Im Mittel benötigte sie <strong><%= $review->{typical_time} %></strong> für eine Entfernung von ca. <strong><%= $review->{typical_km} %> km</strong> (<%= $review->{typical_kmh} %> km/h).
|
||||
</p>
|
||||
% if ($review->{typical_delay_dep} == 0 and $review->{typical_delay_arr} == 0) {
|
||||
<p>Außerdem war sie <strong>komplett pünktlich</strong> (wtf).</p>
|
||||
% }
|
||||
% elsif ($review->{typical_delay_dep} > 0 and $review->{typical_delay_arr} > 0) {
|
||||
<p>Sie fuhr <strong><%= $review->{typical_delay_dep_h} %></strong> zu spät
|
||||
% if ($review->{typical_delay_arr} < $review->{typical_delay_dep}) {
|
||||
ab, konnte aber einen Teil der Verspätung wieder herausholen.
|
||||
Ihr Ziel erreichte sie nur noch <strong><%= $review->{typical_delay_arr_h} %></strong> später als vorgesehen.
|
||||
% }
|
||||
% elsif ($review->{typical_delay_arr} == $review->{typical_delay_dep}) {
|
||||
ab und kam ebenso <strong><%= $review->{typical_delay_arr_h} %></strong> zu spät am Ziel an.
|
||||
% }
|
||||
% else {
|
||||
ab und schlich mit <strong>+<%= $review->{typical_delay_arr} %></strong> ins Ziel.
|
||||
% }
|
||||
% }
|
||||
</div>
|
||||
<div class="carousel-item" href="#three">
|
||||
<h2>High Scores</h2>
|
||||
<p><a href="/journey/<%= $review->{longest_t_id} %>">Längste Zugfahrt</a>:
|
||||
<strong><%= $review->{longest_t_time} %></strong> mit <strong><%= $review->{longest_t_type} %> <%= $review->{longest_t_lineno} %></strong> von <%= $review->{longest_t_from} %> nach <%= $review->{longest_t_to} %>.</p>
|
||||
% if ($review->{longest_km_id} == $review->{longest_t_id}) {
|
||||
<p>Mit <strong><%= $review->{longest_km_km} %> km</strong> war sie gleichzeitig deine weiteste Fahrt.</p>
|
||||
% }
|
||||
% else {
|
||||
<p><a href="/journey/<%= $review->{longest_km_id} %>">Größte Entfernung</a>:
|
||||
<strong><%= $review->{longest_km_km} %> km</strong> mit <strong><%= $review->{longest_km_type} %> <%= $review->{longest_km_lineno} %></strong> von <%= $review->{longest_km_from} %> nach <%= $review->{longest_km_to} %>.</p>
|
||||
% }
|
||||
<p><a href="/journey/<%= $review->{shortest_t_id} %>">Kürzeste Zugfahrt</a>:
|
||||
<strong><%= $review->{shortest_t_time} %></strong> mit <strong><%= $review->{shortest_t_type} %> <%= $review->{shortest_t_lineno} %></strong> von <%= $review->{shortest_t_from} %> nach <%= $review->{shortest_t_to} %>.</p>
|
||||
% if ($review->{shortest_km_id} == $review->{shortest_t_id}) {
|
||||
<p>Mit <strong><%= $review->{shortest_km_m} %> m</strong> war sie gleichzeitig dein kleinster Katzensprung.</p>
|
||||
% }
|
||||
% else {
|
||||
<p><a href="/journey/<%= $review->{shortest_km_id} %>">Kleinster Katzensprung</a>:
|
||||
<strong><%= $review->{shortest_km_m} %> m</strong> mit <strong><%= $review->{shortest_km_type} %> <%= $review->{shortest_km_lineno} %></strong> von <%= $review->{shortest_km_from} %> nach <%= $review->{shortest_km_to} %>.</p>
|
||||
% }
|
||||
</div>
|
||||
% if ($review->{issue1_count}) {
|
||||
<div class="carousel-item" href="#four">
|
||||
<h2>Oepsie Woepsie</h2>
|
||||
<p><strong><%= $review->{issue_percent} %></strong> aller Fahrten liefen nicht wie vorgesehen ab.</p>
|
||||
<p>Die drei häufigsten Anmerkungen waren:</p>
|
||||
% for my $i (1 .. 3) {
|
||||
% if ($review->{"issue${i}_count"}) {
|
||||
<p><strong><%= $review->{"issue${i}_count"} %>×</strong> „<%= $review->{"issue${i}_text"} %>“</p>
|
||||
% }
|
||||
% }
|
||||
</div>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue