station data saving, fixes
This commit is contained in:
parent
bb0ddb40f1
commit
eb59bd978c
@ -3,6 +3,7 @@
|
|||||||
<!--
|
<!--
|
||||||
IDEAS:
|
IDEAS:
|
||||||
- Achievements (i.e. "take a train which is delayed by at least 60 minutes" xd)
|
- Achievements (i.e. "take a train which is delayed by at least 60 minutes" xd)
|
||||||
|
- Bonus bei Verspätung, Gesamtverspätung sammeln
|
||||||
- Show a map in the end (no need for world map, just use coordinates on flat black rectangle)
|
- Show a map in the end (no need for world map, just use coordinates on flat black rectangle)
|
||||||
-->
|
-->
|
||||||
<head>
|
<head>
|
||||||
@ -22,7 +23,12 @@ body {
|
|||||||
background: light-dark(#ECE, #000);
|
background: light-dark(#ECE, #000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.DraggingTop {
|
||||||
|
z-index: 15;
|
||||||
|
}
|
||||||
|
|
||||||
#Header {
|
#Header {
|
||||||
|
z-index: 5;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -271,6 +277,7 @@ function draggingOnDown(x, y, e) {
|
|||||||
if (isInGame) return;
|
if (isInGame) return;
|
||||||
if (draggingElement !== null) return;
|
if (draggingElement !== null) return;
|
||||||
draggingElement = e;
|
draggingElement = e;
|
||||||
|
draggingElement.classList.add("DraggingTop");
|
||||||
draggingStartPos = [x, y];
|
draggingStartPos = [x, y];
|
||||||
}
|
}
|
||||||
function draggingOnMove(x, y) {
|
function draggingOnMove(x, y) {
|
||||||
@ -283,6 +290,7 @@ function draggingOnMove(x, y) {
|
|||||||
}
|
}
|
||||||
function draggingOnUp(x, y) {
|
function draggingOnUp(x, y) {
|
||||||
if (draggingElement === null) return;
|
if (draggingElement === null) return;
|
||||||
|
draggingElement.classList.remove("DraggingTop");
|
||||||
if (!isInGame) {
|
if (!isInGame) {
|
||||||
if (draggingEndCallback) draggingEndCallback(x, y);
|
if (draggingEndCallback) draggingEndCallback(x, y);
|
||||||
}
|
}
|
||||||
@ -558,7 +566,6 @@ async function reloadDepartures() {
|
|||||||
if (evaNumber === stopEvaNumber) foundCurrent = false;
|
if (evaNumber === stopEvaNumber) foundCurrent = false;
|
||||||
}
|
}
|
||||||
for (const [stop, evaNumber] of result[0]) {
|
for (const [stop, evaNumber] of result[0]) {
|
||||||
console.log(stop, ": ", evaNumber);
|
|
||||||
let stopElem = document.createElement("li");
|
let stopElem = document.createElement("li");
|
||||||
stopElem.innerText = stop;
|
stopElem.innerText = stop;
|
||||||
stopElem.style.wordWrap = "nowrap";
|
stopElem.style.wordWrap = "nowrap";
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub struct TransportModesSet(u32);
|
pub struct TransportModesSet(u32);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum TransportMode {
|
pub enum TransportMode {
|
||||||
Bus,
|
Bus,
|
||||||
Tram,
|
Tram,
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
pub mod ratelimit;
|
pub mod ratelimit;
|
||||||
|
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use axum::{Json, extract::Path, response::Html, routing::get};
|
use axum::{Json, extract::Path, response::Html, routing::get};
|
||||||
use rand::seq::IndexedRandom;
|
use rand::seq::IndexedRandom;
|
||||||
use ratelimit::Ratelimit;
|
use ratelimit::Ratelimit;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use tokio::sync::Mutex;
|
use tokio::{sync::Mutex, time::sleep};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bahn_api::{
|
bahn_api::{
|
||||||
@ -28,7 +28,19 @@ pub async fn main() {
|
|||||||
|
|
||||||
let ratelimit = Ratelimit::new(10, 10);
|
let ratelimit = Ratelimit::new(10, 10);
|
||||||
|
|
||||||
let stations = Arc::new(Mutex::new(Stations::new()));
|
let stations = Arc::new(Mutex::new(Stations::load().await.unwrap().unwrap()));
|
||||||
|
{
|
||||||
|
let stations = Arc::clone(&stations);
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
loop {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
sleep(Duration::from_secs(30)).await;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
sleep(Duration::from_secs(900)).await;
|
||||||
|
stations.lock().await.save().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
axum::serve(
|
axum::serve(
|
||||||
tokio::net::TcpListener::bind(
|
tokio::net::TcpListener::bind(
|
||||||
|
@ -1 +1,3 @@
|
|||||||
pub mod stations;
|
pub mod stations;
|
||||||
|
pub mod stations_files;
|
||||||
|
pub mod stations_saving;
|
||||||
|
@ -60,6 +60,9 @@ impl Stations {
|
|||||||
self.stations.insert(eva_number, station);
|
self.stations.insert(eva_number, station);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub(super) fn insert_raw(&mut self, eva_number: StationEvaNumber, station: Station) {
|
||||||
|
self.stations.insert(eva_number, station);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, eva_number: &StationEvaNumber) -> Option<&Station> {
|
pub fn get(&self, eva_number: &StationEvaNumber) -> Option<&Station> {
|
||||||
self.stations.get(eva_number)
|
self.stations.get(eva_number)
|
||||||
|
71
src/storage/stations_files.rs
Normal file
71
src/storage/stations_files.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::bahn_api::{
|
||||||
|
basic_types::station_ids::{DbStopId, StationEvaNumber},
|
||||||
|
transport_modes::{TransportMode, TransportModesSet},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::stations::Station;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct StationJsonRef<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
db_station_id: &'a str,
|
||||||
|
related_stations: Vec<&'a str>,
|
||||||
|
lat_lon: Option<(f64, f64)>,
|
||||||
|
transport_modes: Vec<TransportMode>,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct StationJson {
|
||||||
|
name: String,
|
||||||
|
db_station_id: String,
|
||||||
|
related_stations: Vec<String>,
|
||||||
|
lat_lon: Option<(f64, f64)>,
|
||||||
|
transport_modes: Vec<TransportMode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn station_to_string(
|
||||||
|
Station {
|
||||||
|
name,
|
||||||
|
db_station_id,
|
||||||
|
related_stations,
|
||||||
|
lat_lon,
|
||||||
|
transport_modes,
|
||||||
|
has_been_changed: _,
|
||||||
|
}: &Station,
|
||||||
|
) -> String {
|
||||||
|
serde_json::to_string(&StationJsonRef {
|
||||||
|
name: &name,
|
||||||
|
db_station_id: &db_station_id.0,
|
||||||
|
related_stations: related_stations.iter().map(|v| v.0.as_str()).collect(),
|
||||||
|
lat_lon: *lat_lon,
|
||||||
|
transport_modes: transport_modes
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| v.iter().collect())
|
||||||
|
.unwrap_or_else(Vec::default),
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn station_from_string(string: &str) -> Result<Station, serde_json::Error> {
|
||||||
|
let StationJson {
|
||||||
|
name,
|
||||||
|
db_station_id,
|
||||||
|
related_stations,
|
||||||
|
lat_lon,
|
||||||
|
transport_modes,
|
||||||
|
} = serde_json::from_str(string)?;
|
||||||
|
Ok(Station {
|
||||||
|
name,
|
||||||
|
db_station_id: DbStopId(db_station_id),
|
||||||
|
related_stations: related_stations.into_iter().map(StationEvaNumber).collect(),
|
||||||
|
lat_lon,
|
||||||
|
transport_modes: Some(
|
||||||
|
transport_modes
|
||||||
|
.into_iter()
|
||||||
|
.fold(TransportModesSet::new(), |set, m| set.with(m)),
|
||||||
|
)
|
||||||
|
.filter(|set| set.len() > 0),
|
||||||
|
has_been_changed: false,
|
||||||
|
})
|
||||||
|
}
|
57
src/storage/stations_saving.rs
Normal file
57
src/storage/stations_saving.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use crate::bahn_api::basic_types::station_ids::StationEvaNumber;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
stations::Stations,
|
||||||
|
stations_files::{station_from_string, station_to_string},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Stations {
|
||||||
|
pub async fn save(&mut self) {
|
||||||
|
for (key, station) in self.stations.iter_mut() {
|
||||||
|
if station.has_been_changed {
|
||||||
|
eprintln!("Saving station {:?} ({:?}).", station.name, key.0);
|
||||||
|
station.has_been_changed = false;
|
||||||
|
if key.0.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
||||||
|
if let Err(e) = tokio::fs::write(
|
||||||
|
format!("stations/{}", key.0.as_str()),
|
||||||
|
station_to_string(station),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"[stations/save] could not save station {:?}: {e}.",
|
||||||
|
station.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"[stations/save] skipping station {:?} because key {:?} contained at least one non-ascii-alphanumeric character.",
|
||||||
|
station.name, key.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load() -> Result<Result<Self, serde_json::Error>, tokio::io::Error> {
|
||||||
|
let mut dir_entries = tokio::fs::read_dir("stations/").await?;
|
||||||
|
let mut s = Self::new();
|
||||||
|
while let Some(file) = dir_entries.next_entry().await? {
|
||||||
|
if let Some(name) = file
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.filter(|name| name.chars().all(|ch| ch.is_ascii_alphanumeric()))
|
||||||
|
{
|
||||||
|
let eva_number = StationEvaNumber(name.to_owned());
|
||||||
|
let mut station =
|
||||||
|
match station_from_string(&tokio::fs::read_to_string(file.path()).await?) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Ok(Err(e)),
|
||||||
|
};
|
||||||
|
station.has_been_changed = false;
|
||||||
|
s.insert_raw(eva_number, station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Ok(s))
|
||||||
|
}
|
||||||
|
}
|
1
stations/.gitkeep
Normal file
1
stations/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
station data goes here, program will not start without this folder
|
Loading…
x
Reference in New Issue
Block a user