diff --git a/migration/src/m20240217_000000_create_a b/migration/src/m20240217_000000_create_a deleted file mode 100644 index e69de29..0000000 diff --git a/migration/src/m20240217_000000_create_b b/migration/src/m20240217_000000_create_b deleted file mode 100644 index e69de29..0000000 diff --git a/migration/src/m20240217_000000_create_route_stop_relation_table.rs b/migration/src/m20240217_000000_create_route_stop_relation_table.rs new file mode 100644 index 0000000..0546535 --- /dev/null +++ b/migration/src/m20240217_000000_create_route_stop_relation_table.rs @@ -0,0 +1,45 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20240217_000000_create_route_table" // Make sure this matches with the file name + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(RouteStopRelation::Table) + .col( + ColumnDef::new(RouteStopRelation::RouteId) + .integer() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(RouteStopRelationRoute::Name).string().not_null()) + .col(ColumnDef::new(RouteStopRelationBakery::ProfitMargin).double().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Bakery table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Bakery::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum RouteStopRelation{ + Table, + RouteId, + StopId, +} diff --git a/migration/src/m20240217_000000_create_route_table.rs b/migration/src/m20240217_000000_create_route_table.rs index e69de29..1a9f947 100644 --- a/migration/src/m20240217_000000_create_route_table.rs +++ b/migration/src/m20240217_000000_create_route_table.rs @@ -0,0 +1,45 @@ +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20240217_000000_create_route_table" // Make sure this matches with the file name + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Route::Table) + .col( + ColumnDef::new(Route::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Route::Name).string().not_null()) + .to_owned(), + ) + .await + } + + // Define how to rollback this migration: Drop the Bakery table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Bakery::Table).to_owned()) + .await + } +} + +#[derive(Iden)] +pub enum Route { + Table, + Id, + Name, +} diff --git a/src/main.rs b/src/main.rs index b6d0387..037fb57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ extern crate tokio; extern crate reqwest; extern crate tide; extern crate anyhow; -extern crate sea_orm; +//extern crate sea_orm; mod state; +mod route; +mod stop; use tide::prelude::*; @@ -13,7 +15,8 @@ async fn main() -> anyhow::Result<()> { let mut app = tide::with_state(state::State::new().await?); app.at("/path").get(transit_path); - app.at("/time-to-arrive").get(transit_estimate); + app.at("/time_to_arrive").get(transit_estimate); + app.at("/route_draw").get(bus_route_draw); // TODO: change to 0.0.0.0 when docker image done app.listen("127.0.0.1:8080").await?; @@ -21,12 +24,51 @@ async fn main() -> anyhow::Result<()> { } -/// return the best path with the request specifying ISO time, lat and longitude in query params +/// return the best path of buses with the request specifying ISO time, lat and longitude in query params +/// Example return +/// { +/// "path": [ +/// { +/// "route_name": "WS Outbound", +/// "enter_stop_id": 3, +/// "exit_stop_id": 10, +/// "enter_stop_coords": {"lat":23.3512, "lon": 1231.123123} +/// }, +/// ... +/// ] +/// } async fn transit_path(mut req: tide::Request)->tide::Result{ Ok("".into()) } -/// return the num of estimated seconds for bus id and stop ids given in query params +/// return the num of estimated seconds for stop ids given in query params +/// example return: +/// 23 async fn transit_estimate(mut req: tide::Request)->tide::Result{ Ok("".into()) } + +/// return the coord pairs as json and line color forgiven route id in query params +/// Example return: +/// {"color":"#0000ff", "stops":[[-75,23],[-21,72]]} +async fn bus_route_draw(mut req: tide::Request)->tide::Result{ + match req.query::()?.id.split("_").collect::>()[..] { + ["OCCT", id] => { + let route = route::occt_route_get(id.parse()); + }, + ["BC", id] => { + let route = route::broome_county_route_get(id.parse()); + }, + _ => { + return Ok(tide::Response::builder(400).body("Error: invalid id").into()) + } + + } + Ok("".into()) +} + +#[derive(serde::Deserialize)] +struct RouteDrawQuery{ + id: String +} + diff --git a/src/route.rs b/src/route.rs new file mode 100644 index 0000000..decb19a --- /dev/null +++ b/src/route.rs @@ -0,0 +1,91 @@ + +struct Route { + id:u32, + name: String, + short_name: String, + color: String, + poly_line: String, + stops: Vec +} +#[derive(serde::Deserialize)] +struct OcctRoute { + id:u32, + name: String, + #[serde(rename(deserialize = "abbr"))] + short_name: String, + #[serde(rename(deserialize = "encLine"))] + poly_line: String, + stops:Vec, + color: String +} +//impl From for Route{ +// fn from(OcctRoute{id,name,short_name,poly_line,stops, color}: OcctRoute) -> Self { +// Self { +// id, +// name, +// short_name, +// color, +// poly_line, +// +// } +// } +//} +#[derive(serde::Deserialize)] +struct BCRoute{ + id:u32, + name: String, + short_name: String, + color: String, + path: Vec, + active: bool, + stops:Vec +} +#[derive(serde::Deserialize)] +struct OcctWrap{ + get_routes:Vec +} +async fn occt_routes_get()->anyhow::Result>{ + let resp = reqwest::get("http://binghamtonupublic.etaspot.net/service.php?service=get_routes&token=TESTING").await?; + let body = resp.text().await?; + let OcctWrap{get_routes: routes} = serde_json::from_str(body.as_str())?; + let stops = crate::stop::occt_stops_get().await?; + let routes = routes.into_iter().map(|route|{ + let stops = stops.iter() + .filter(|stop|{ + route.stops.iter().any(|id|*id==stop.id) + }).map(Clone::clone).collect(); + + Route{ + id:route.id, + color:route.color, + name:route.name, + poly_line:route.poly_line, + short_name: route.short_name, + stops + } + }).collect(); + Ok(routes) + +} +async fn broom_county_routes_get()->anyhow::Result{ + let resp = reqwest::get("https://bctransit.doublemap.com/map/v2/routes").await?; + let body = resp.text().await?; + let routes:Vec = serde_json::from_str(body.as_str())?; + let stops = crate::stop::BC_stops_get().await?; + let routes = routes.into_iter().map(|route|{ + let stops = stops.iter() + .filter(|stop|{ + route.stops.iter().any(|id|*id==stop.id) + }).map(Clone::clone).collect(); + let poly_line = crate::stop::poly_encode_stops(stops).await; + Route{ + id:route.id, + color:route.color, + name:route.name, + poly_line, + short_name: route.short_name, + stops + } + }).collect(); + Ok(routes) +} diff --git a/src/state.rs b/src/state.rs index 0876421..d280985 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,13 +1,13 @@ #[derive(Clone)] pub struct State{ - pub db: sea_orm::DatabaseConnection + //pub db: sea_orm::DatabaseConnection } const DEFAULT_DB_CONN:&'static str = "sqlite://default_db.db"; impl State{ pub async fn new() -> anyhow::Result { Ok(Self{ - db: sea_orm::Database::connect(DEFAULT_DB_CONN).await? + //db: sea_orm::Database::connect(DEFAULT_DB_CONN).await? }) } } diff --git a/src/stop.rs b/src/stop.rs new file mode 100644 index 0000000..de83dea --- /dev/null +++ b/src/stop.rs @@ -0,0 +1,69 @@ +#[derive(Clone)] +pub struct Stop{ + pub id:u32, + pub name:String, + pub lat: f64, + pub lon: f64, +} + +#[derive(serde::Deserialize)] +struct OcctStop{ + id:u32, + name:String, + lat: f64, + #[serde(rename(deserialize = "lng"))] + lon: f64, +} +impl From for Stop{ + fn from(OcctStop{id,name,lat,lon}: OcctStop) -> Self { + Self { + id, + name, + lat, + lon + } + } +} +#[derive(serde::Deserialize)] +struct BCStop{ + id:u32, + name:String, + lat: f64, + lon: f64, +} +impl From for Stop{ + fn from(BCStop{id,name,lat,lon}: BCStop) -> Self { + Self { + id, + name, + lat, + lon + } + } +} + +pub async fn occt_stops_get()->anyhow::Result>{ + Ok(Vec::new()) +} +pub async fn BC_stops_get()->anyhow::Result>{ + Ok(Vec::new()) +} +pub fn poly_encode_stops>(stops:I)->String{ + stops.into_iter() + .map(|stop|{ + enc_float(stop.lat) + }) + .reduce(|s1,s2|s1+&s2) +fn enc_float(num:f64)->String{ + let mut working:i32 = (num*1e5).round(); + //hopethis does what's needed + working<<=1; + if num < 0 { + working = !working; + } + let mut bits:[bool;30] = [false;30]; + for i in 0..30{ + bits[i] = working % 2 == 1; + working >>=1; + } +}