From 46b32e7df4d9ff89674217944b1f301c46e20e3a Mon Sep 17 00:00:00 2001 From: stuebinm Date: Thu, 21 Apr 2022 22:07:49 +0200 Subject: add import from hafas to travelynx (using yuka's hafas-rs) --- src/main.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 10 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index f41d871..2f3a34e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,18 @@ +use chrono::{DateTime, Utc}; use clap::{Parser, Subcommand}; use colored::*; use serde::Deserialize; +use serde_json::json; use traveltext::onboard::{choose_api, OnBoardAPI}; use traveltext::{traits::*, travelynx::*, types::*}; +use hafas_rs::api::journeys::*; +use hafas_rs::api::locations::*; +use hafas_rs::client::HafasClient; +use hafas_rs::profile::db::DbProfile; +use hafas_rs::requester::hyper::HyperRustlsRequester; +use hafas_rs::{Place, ProductsSelection}; #[derive(Parser)] struct Cli { @@ -15,8 +23,6 @@ struct Cli { debug: bool, #[clap(default_value = "https://travelynx.de")] baseurl: String, - /// API token to use in requests - token: Option } #[derive(Subcommand)] @@ -40,8 +46,21 @@ enum Command { From { from: String }, - Station { - station: String + /// Find a (single) trip of a single train via HAFAS. + /// Mostly intended to ferry trip-data into travelynx 'by hand' if it doesn't know some connection + Journey { + /// starting station's name (fuzzy-matched) + from_name: String, + /// destination station's name (fuzzy-matched) + to_name: String, + /// departure time; takes the next available connection + time: Option>, + /// set the dryRun flag to 'true' when importing to travelynx + #[clap(short = 'd', long = "dry-run")] + dry_run: bool, + /// import the found trip into travelynx (needs the token_import config option!) + #[clap(short = 'i', long = "import")] + import: bool }, Arewethereyet, /// If iceportal.de is available, ask it which train we're in and @@ -60,10 +79,12 @@ enum Command { #[derive(Deserialize)] struct Config { token_status: String, - token_travel: String + token_travel: String, + token_import: Option } -fn main() -> Result<(), ureq::Error> { +#[tokio::main] +async fn main() -> Result<(), ureq::Error> { let cli = Cli::parse(); let traveltext = format!( @@ -152,7 +173,11 @@ fn main() -> Result<(), ureq::Error> { println!("{}: {}", traveltext, resp); } Command::From { from } => { - println!("{}: {}", traveltext, "warning: this command may pollute your trip history".red()); + println!( + "{}: {}", + traveltext, + "warning: this command may pollute your trip history".red() + ); let status: Status = exiting_get_request( &format!("{}/api/v1/status/{}", cli.baseurl, config.token_status), cli.debug @@ -251,9 +276,108 @@ fn main() -> Result<(), ureq::Error> { } } } - Command::Station { station } => { - // let c = HafasClient::new(DbProfile, HyperRustlsRequester::new()); - // println!("{:#?}", c.suggestions("München Hbf", None).await.unwrap()); + Command::Journey { + from_name, + to_name, + time, + dry_run, + import + } => { + let c = HafasClient::new(DbProfile, HyperRustlsRequester::new()); + let from = &c + .locations(LocationsOptions { + query: from_name, + results: None, + // TODO: respect locale set + language: Some("de".to_string()) + }) + .await + .unwrap()[0]; + + let to = &c + .locations(LocationsOptions { + query: to_name, + results: None, + language: None + }) + .await + .unwrap()[0]; + + let opts = JourneysOptions { + products: ProductsSelection { + bus: Some(false), + ..ProductsSelection::default() + }, + departure: time.map(|t| t.timestamp()), //Some(1650536340); + stopovers: Some(true), + language: Some("de".to_string()), + ..JourneysOptions::default() + }; + + let journey = &c + .journeys(from.clone(), to.clone(), opts) + .await + .unwrap() + .journeys[0] + .legs[0]; + + let stops = journey.stopovers.as_ref().unwrap(); + + println!("{}: found this trip:", traveltext); + + for stop in stops { + match &stop.stop { + Place::Stop(station) => { + println!("{}", station.name.as_ref().unwrap()) + } + _ => panic!("this train stops somewhere that's not a station??") + } + stop.arrival.map(|t| println!("arr: {:?}", t.to_rfc3339())); + stop.departure.map(|t| println!("dep: {:?}", t.to_rfc3339())); + } + + if import { + let travelynx = json!({ + "token": config.token_import.unwrap(), + "dryRun": dry_run, + "fromStation": match &stops[0].stop { + Place::Stop (station) => json!({ + "name":station.name, + "scheduledTime":stops[0].departure.unwrap().timestamp() + }), + _ => panic!("this trip lacks a first station?") + }, + "toStation": match &stops[stops.len()-1].stop { + Place::Stop (station) => json!({ + "name": station.name, + "scheduledTime": stops[stops.len()-1].arrival.unwrap().timestamp() + }), + _ => panic!("this trip lacks an end station?") + }, + "train": match &journey.line { + Some(line) => json!({ + "type": line.name.as_ref().unwrap().split_ascii_whitespace().next().unwrap(), + "line": serde_json::Value::Null, + "no": line.fahrt_nr.as_ref().unwrap() + }), + None => panic!("could not find train information") + }, + "intermediateStops": &stops[1..stops.len()-1] + .iter() + .filter_map(|s| match &s.stop { + Place::Stop (station) => station.name.as_ref(), + _ => None + }) + .collect::>() + }); + let resp: serde_json::Value = exiting_post_request( + &format!("{}/api/v1/import", cli.baseurl), + travelynx, + cli.debug + ); + + println!("{:?}", resp); + } } } Ok(()) -- cgit v1.2.3