clean up + update dfile
All checks were successful
Create and publish a Docker image / build-and-push-image (push) Successful in 6m0s

This commit is contained in:
Nicholas Orlowsky 2025-09-21 19:53:11 -04:00
parent 7904c80642
commit 8fd41b1090
No known key found for this signature in database
GPG key ID: A9F3BA4C0AA7A70B
8 changed files with 314 additions and 335 deletions

152
api/src/templates.rs Normal file
View file

@ -0,0 +1,152 @@
use libseptastic::{direction::Direction, stop_schedule::{Trip, TripTracking}};
use std::{cmp::Ordering, collections::BTreeMap};
use serde::{Serialize};
use libseptastic::stop_schedule::TripTracking::Tracked;
#[derive(askama::Template)]
#[template(path = "layout.html")]
pub struct ContentTemplate<T: askama::Template> {
pub content: T,
pub page_title: Option<String>,
pub page_desc: Option<String>,
}
#[derive(askama::Template)]
#[template(path = "route.html")]
pub struct RouteTemplate {
pub route: libseptastic::route::Route,
pub directions: Vec<libseptastic::direction::Direction>,
pub timetables: Vec<TimetableDirection>,
pub filter_stops: Option<Vec<i64>>
}
#[derive(askama::Template)]
#[template(path = "routes.html")]
pub struct RoutesTemplate {
pub rr_routes: Vec<libseptastic::route::Route>,
pub subway_routes: Vec<libseptastic::route::Route>,
pub trolley_routes: Vec<libseptastic::route::Route>,
pub bus_routes: Vec<libseptastic::route::Route>
}
#[derive(askama::Template)]
#[template(path = "index.html")]
pub struct IndexTemplate {
}
#[derive(Debug, Serialize)]
pub struct TimetableStopRow {
pub stop_id: i64,
pub stop_name: String,
pub stop_sequence: i64,
pub times: Vec<Option<i64>>
}
#[derive(Debug, Serialize)]
pub struct TimetableDirection {
pub direction: Direction,
pub trip_ids: Vec<String>,
pub tracking_data: Vec<TripTracking>,
pub rows: Vec<TimetableStopRow>
}
pub fn build_timetables(
directions: &[Direction],
trips: &[Trip],
) -> Vec<TimetableDirection> {
let mut results = Vec::new();
for direction in directions {
let mut direction_trips: Vec<&Trip> = trips
.iter()
.filter(|trip| trip.direction_id == direction.direction_id)
.collect();
direction_trips.sort_by_key(|trip| {
trip.schedule
.iter()
.filter_map(|s| Some(s.arrival_time))
.min()
.unwrap_or(i64::MAX)
});
let trip_ids: Vec<String> = direction_trips
.iter()
.map(|t| t.trip_id.clone())
.collect();
let live_trips: Vec<TripTracking> = direction_trips
.iter()
.map(|t| t.tracking_data.clone())
.collect();
let mut stop_map: BTreeMap<i64, (i64, String, Vec<Option<i64>>)> = BTreeMap::new();
for (trip_index, trip) in direction_trips.iter().enumerate() {
for stop in &trip.schedule {
let entry = stop_map
.entry(stop.stop_id)
.or_insert((stop.stop_sequence, stop.stop_name.clone(), vec![None; direction_trips.len()]));
// If this stop_id appears in multiple trips with different sequences, keep the lowest
entry.0 = entry.0.max(stop.stop_sequence);
entry.1 = stop.stop_name.clone();
entry.2[trip_index] = Some(stop.arrival_time);
}
}
let mut rows: Vec<TimetableStopRow> = stop_map
.into_iter()
.map(|(stop_id, (stop_sequence, stop_name, times))| TimetableStopRow {
stop_id,
stop_sequence,
stop_name,
times,
})
.collect();
rows.sort_by(| a, b| {
if a.stop_sequence < b.stop_sequence {
Ordering::Less
} else {
Ordering::Greater
}
});
assert!(trip_ids.len() == live_trips.len());
for row in &rows {
assert!(row.times.len() == live_trips.len());
}
results.push(TimetableDirection {
direction: direction.clone(),
trip_ids,
rows,
tracking_data: live_trips
});
}
results
}
mod filters {
pub fn format_time(
seconds_since_midnight: &i64,
_: &dyn askama::Values,
) -> askama::Result<String> {
let total_minutes = seconds_since_midnight / 60;
let (hours, ampm) = {
let hrs = total_minutes / 60;
if hrs > 12 {
(hrs - 12, "PM")
} else {
(hrs, "AM")
}
};
let minutes = total_minutes % 60;
Ok(format!("{}:{:02} {}", hours, minutes, ampm))
}
}