diff options
author | 2022-08-15 22:21:27 -0700 | |
---|---|---|
committer | 2022-08-15 22:21:27 -0700 | |
commit | 5805ef77fef8b9b3a2f83a71091f83651a49dd89 (patch) | |
tree | dbf3eba1fd5ea7bc34f50c4e05f1101681fd0541 | |
parent | edc5985b1744dcffb8bf1b41ce273508573589fe (diff) | |
download | touchpad-5805ef77fef8b9b3a2f83a71091f83651a49dd89.tar.gz touchpad-5805ef77fef8b9b3a2f83a71091f83651a49dd89.tar.zst touchpad-5805ef77fef8b9b3a2f83a71091f83651a49dd89.zip |
Adds individual_event endpoint to touchpad live
Also moves Touchpad conversions into conversion.rs and into its own
FromTouchpadLive & TryFromTouchpadLive traits.
-rw-r--r-- | rust/proto/src/lib.rs | 49 | ||||
-rw-r--r-- | rust/scraper/src/main.rs | 3 | ||||
-rw-r--r-- | rust/scraper/src/touchpad/conversion.rs | 58 | ||||
-rw-r--r-- | rust/scraper/src/touchpad/error.rs | 4 | ||||
-rw-r--r-- | rust/scraper/src/touchpad/mod.rs | 75 | ||||
-rw-r--r-- | rust/scraper/src/touchpad/request_response.rs | 4 |
6 files changed, 139 insertions, 54 deletions
diff --git a/rust/proto/src/lib.rs b/rust/proto/src/lib.rs index 1a16c15..1a8199b 100644 --- a/rust/proto/src/lib.rs +++ b/rust/proto/src/lib.rs @@ -1,55 +1,12 @@ -use chrono::{DateTime, TimeZone, Utc}; +use chrono::{DateTime, Utc}; use std::ops::{Deref, DerefMut}; include!("gen/mod.rs"); -impl From<&str> for touchpad::common::v1::Gender { - fn from(s: &str) -> Self { - match s { - "Male" => Self::Male, - "Female" => Self::Female, - _ => Self::Unspecified, - } - } -} - -impl From<&str> for touchpad::common::v1::Stroke { - fn from(s: &str) -> Self { - match s { - "Fly" => Self::Fly, - "Back" => Self::Back, - "Breast" => Self::Breast, - "Free" => Self::Free, - "Medley" => Self::Medley, - _ => Self::Unspecified, - } - } -} - -impl From<&str> for touchpad::common::v1::EventTimeResult { - fn from(s: &str) -> Self { - match s { - "DQ" => Self::Dq, - "DNS" => Self::Dns, - "SCR" => Self::Scr, - _ => Self::Unspecified, - } - } -} +//region ProtoTimestamp pub struct ProtoTimestamp(pbjson_types::Timestamp); -impl ProtoTimestamp { - const TOUCHPAD_DATE_FORMAT: &'static str = "%Y-%m-%d"; - - pub fn from_touchpad(str: &str) -> Result<Self, chrono::format::ParseError> { - let date = chrono::NaiveDate::parse_from_str(str, ProtoTimestamp::TOUCHPAD_DATE_FORMAT)?; - let datetime = chrono::NaiveDateTime::new(date, chrono::NaiveTime::from_hms(0, 0, 0)); - - Ok(Utc.from_utc_datetime(&datetime).into()) - } -} - impl Into<pbjson_types::Timestamp> for ProtoTimestamp { fn into(self) -> pbjson_types::Timestamp { self.0 @@ -75,3 +32,5 @@ impl DerefMut for ProtoTimestamp { &mut self.0 } } + +//endregion diff --git a/rust/scraper/src/main.rs b/rust/scraper/src/main.rs index 80a2f8f..7d6a4a2 100644 --- a/rust/scraper/src/main.rs +++ b/rust/scraper/src/main.rs @@ -15,5 +15,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { let swimmers = client.swimmers(18618).await?; println!("{:?}", swimmers); + let event = client.individual_event(18618, 1031911).await?; + println!("{:?}", event); + Ok(()) }
\ No newline at end of file diff --git a/rust/scraper/src/touchpad/conversion.rs b/rust/scraper/src/touchpad/conversion.rs new file mode 100644 index 0000000..6c7b550 --- /dev/null +++ b/rust/scraper/src/touchpad/conversion.rs @@ -0,0 +1,58 @@ +use chrono::{NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; + +pub trait FromTouchpadLive<T>: Sized { + fn from_touchpad(value: T) -> Self; +} + +pub trait TryFromTouchpadLive<T>: Sized { + type Error; + + fn try_from_touchpad(value: T) -> Result<Self, Self::Error>; +} + +impl FromTouchpadLive<&str> for proto::touchpad::common::v1::Gender { + fn from_touchpad(value: &str) -> Self { + match value { + "Male" => Self::Male, + "Female" => Self::Female, + _ => Self::Unspecified, + } + } +} + +impl FromTouchpadLive<&str> for proto::touchpad::common::v1::Stroke { + fn from_touchpad(value: &str) -> Self { + match value { + "Fly" => Self::Fly, + "Back" => Self::Back, + "Breast" => Self::Breast, + "Free" => Self::Free, + "Medley" => Self::Medley, + _ => Self::Unspecified, + } + } +} + +impl FromTouchpadLive<&str> for proto::touchpad::common::v1::EventTimeResult { + fn from_touchpad(value: &str) -> Self { + match value { + "DQ" => Self::Dq, + "DNS" => Self::Dns, + "SCR" => Self::Scr, + _ => Self::Unspecified, + } + } +} + +impl TryFromTouchpadLive<&str> for proto::ProtoTimestamp { + type Error = chrono::format::ParseError; + + fn try_from_touchpad(value: &str) -> Result<Self, Self::Error> { + const TOUCHPAD_DATE_FORMAT: &'static str = "%Y-%m-%d"; + + let date = NaiveDate::parse_from_str(value, TOUCHPAD_DATE_FORMAT)?; + let datetime = NaiveDateTime::new(date, NaiveTime::from_hms(0, 0, 0)); + + Ok(Utc.from_utc_datetime(&datetime).into()) + } +}
\ No newline at end of file diff --git a/rust/scraper/src/touchpad/error.rs b/rust/scraper/src/touchpad/error.rs index 57c2eda..b9216e4 100644 --- a/rust/scraper/src/touchpad/error.rs +++ b/rust/scraper/src/touchpad/error.rs @@ -9,5 +9,7 @@ pub enum TouchpadLiveError { #[error("Abort Error")] AbortError(#[from] tokio::task::JoinError), #[error("Integer Parse Error")] - ParseError(#[from] std::num::ParseIntError) + ParseError(#[from] std::num::ParseIntError), + #[error("Not Found")] + NotFoundError, }
\ No newline at end of file diff --git a/rust/scraper/src/touchpad/mod.rs b/rust/scraper/src/touchpad/mod.rs index fdbd591..6d965eb 100644 --- a/rust/scraper/src/touchpad/mod.rs +++ b/rust/scraper/src/touchpad/mod.rs @@ -1,5 +1,6 @@ mod error; mod request_response; +mod conversion; use futures::{future, TryFutureExt}; use proto::touchpad::common::v1; @@ -9,10 +10,11 @@ use std::collections::HashMap; pub use error::TouchpadLiveError; use proto::ProtoTimestamp; -use crate::touchpad::request_response::{ - EventsResponse, ParticipantResponse, TeamInfoResponse, -}; +use crate::touchpad::request_response::{EventsResponse, EventTimeResponse, ParticipantResponse, TeamInfoResponse}; use request_response::MeetInfoResponse; +use crate::touchpad::conversion::TryFromTouchpadLive; + +use self::conversion::FromTouchpadLive; const BASE_URL: &'static str = "https://www.touchpadlive.com/rest/touchpadlive"; @@ -57,8 +59,8 @@ impl TouchpadLiveClient { Ok(v1::SwimMeet { id, meet_name: resp.meet_name, - start: Some(ProtoTimestamp::from_touchpad(&resp.start_date)?.into()), - end: Some(ProtoTimestamp::from_touchpad(&resp.end_date)?.into()), + start: Some(ProtoTimestamp::try_from_touchpad(&resp.start_date)?.into()), + end: Some(ProtoTimestamp::try_from_touchpad(&resp.end_date)?.into()), teams: teams .into_iter() .map(|t| v1::Team { @@ -79,10 +81,11 @@ impl TouchpadLiveClient { age_hi: ev.age_hi as u32, age_lo: ev.age_lo as u32, distance: ev.distance, + gender: v1::Gender::from_touchpad (ev.stroke.as_str()) as i32, event: None, event_num: ev.event_number.parse()?, meet_id: id, - stroke: v1::Stroke::from(ev.stroke.as_str()) as i32, + stroke: v1::Stroke::from_touchpad(ev.stroke.as_str()) as i32, })) .collect() } @@ -113,6 +116,23 @@ impl TouchpadLiveClient { Ok(process_swimmers(swimmers, &swimmer_map, events)) } + + pub async fn individual_event(&self, meet_id: u32, ev_id: u32) -> Result<v1::Event, TouchpadLiveError> { + let (events, times) = { + let events = get_events(self.client.clone(), meet_id); + + let event = get_event(self.client.clone(), "individual", meet_id, ev_id); + + future::try_join(events, event) + }.await?; + + let event = events + .into_iter() + .find(|ev| ev.id == ev_id) + .ok_or(TouchpadLiveError::NotFoundError)?; + + create_individual_event_proto(meet_id, event, times) + } } async fn get_events(client: Client, id: u32) -> Result<Vec<EventsResponse>, TouchpadLiveError> { @@ -124,6 +144,15 @@ async fn get_events(client: Client, id: u32) -> Result<Vec<EventsResponse>, Touc .await?) } +async fn get_event(client: Client, tp: &str, meet_id: u32, ev_id: u32) -> Result<Vec<EventTimeResponse>, TouchpadLiveError> { + Ok(client + .get(format!("{}/meets/{}/{}/{}", BASE_URL, meet_id, tp, ev_id)) + .send() + .await? + .json() + .await?) +} + fn events_to_map<I: IntoIterator<Item=EventsResponse>>(events: I) -> HashMap<u32, EventsResponse> { events.into_iter().map(|ev| (ev.id, ev)).collect() } @@ -146,7 +175,7 @@ fn process_swimmers( for event in swimmer_events.unwrap() { let ev_info_result = event_map.get(event); if let Some(ev_info) = ev_info_result { - gender = ev_info.gender.as_str().into(); + gender = v1::Gender::from_touchpad(ev_info.gender.as_str()); if gender != v1::Gender::Unspecified { break; } @@ -166,3 +195,35 @@ fn process_swimmers( proto_swimmers } + +fn create_individual_event_proto( + meet_id: u32, + event: EventsResponse, + times: Vec<EventTimeResponse>, +) -> Result<v1::Event, TouchpadLiveError> { + let ev_id = event.id; + + let proto_times = times + .into_iter() + .filter_map(|t| Some(v1::individual_event::IndividualEventTime { + swimmer_id: t.swimmer_id?, + time: Some(t.into_proto(ev_id)), + })) + .collect(); + + Ok(v1::Event { + age_hi: event.age_hi as u32, + age_lo: event.age_lo as u32, + distance: event.distance, + event_num: event.event_number.parse()?, + gender: v1::Gender::from_touchpad(event.gender.as_str()) as i32, + id: ev_id, + meet_id, + stroke: v1::Stroke::from_touchpad(event.stroke.as_str()) as i32, + event: Some(v1::event::Event::Individual( + v1::IndividualEvent { + times: proto_times, + } + )), + }) +} diff --git a/rust/scraper/src/touchpad/request_response.rs b/rust/scraper/src/touchpad/request_response.rs index 67b8a0b..cafdd60 100644 --- a/rust/scraper/src/touchpad/request_response.rs +++ b/rust/scraper/src/touchpad/request_response.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use super::conversion::FromTouchpadLive; + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct MeetInfoResponse { @@ -92,7 +94,7 @@ impl EventTimeResponse { }; let result = { - let result = proto::touchpad::common::v1::EventTimeResult::from( + let result = proto::touchpad::common::v1::EventTimeResult::from_touchpad( self.final_formatted_time.as_str(), ); |