historical seasons

This commit is contained in:
Robin Appelman 2023-11-18 21:10:37 +01:00
commit e20a9cdae9
8 changed files with 3064 additions and 3 deletions

View file

@ -173,3 +173,17 @@ pub enum MatchResult {
},
ByeWeek,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Seasons {
pub mode: String,
pub seasons: Vec<Season>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Season {
pub id: String,
pub name: String,
}

View file

@ -3,9 +3,9 @@ mod error;
#[doc(hidden)]
pub mod parser;
use crate::data::{MembershipHistory, Player, RosterHistory, Team, TeamSeason};
use crate::data::{MembershipHistory, Player, RosterHistory, Seasons, Team, TeamSeason};
use crate::parser::{
Parser, PlayerDetailsParser, PlayerParser, TeamMatchesParser, TeamParser,
Parser, PlayerDetailsParser, PlayerParser, SeasonsParser, TeamMatchesParser, TeamParser,
TeamRosterHistoryParser,
};
pub use error::*;
@ -23,6 +23,7 @@ pub struct UgcClient {
team_parser: TeamParser,
team_roster_history_parser: TeamRosterHistoryParser,
team_matches_parser: TeamMatchesParser,
seasons_parser: SeasonsParser,
}
/// "API client" for ugc by scraping the website
@ -35,6 +36,7 @@ impl UgcClient {
team_parser: TeamParser::new(),
team_roster_history_parser: TeamRosterHistoryParser::new(),
team_matches_parser: TeamMatchesParser::new(),
seasons_parser: SeasonsParser::new(),
}
}
@ -116,4 +118,16 @@ impl UgcClient {
.await?;
self.team_matches_parser.parse(&body)
}
/// Get all historical seasons by game mode
pub async fn previous_seasons(&self) -> Result<Vec<Seasons>> {
let body = self
.client
.get("https://www.ugcleague.com")
.send()
.await?
.text()
.await?;
self.seasons_parser.parse(&body)
}
}

View file

@ -6,12 +6,14 @@ use time::macros::format_description;
mod player;
mod player_details;
mod seasons;
mod team;
mod team_matches;
mod team_roster_history;
pub use player::*;
pub use player_details::*;
pub use seasons::*;
pub use team::*;
pub use team_matches::*;
pub use team_roster_history::*;

84
src/parser/seasons.rs Normal file
View file

@ -0,0 +1,84 @@
use super::Parser;
use crate::data::{Season, Seasons};
use crate::parser::{select_text, ElementExt};
use crate::{ParseError, Result};
use scraper::{Html, Selector};
const SELECTOR_MENU: &str = ".sub-menu";
const SELECTOR_NAME: &str = ".mega-menu-sub-title";
const SELECTOR_SEASON_LINK: &str = "ul[id$=\"seasons\"] a[href^=\"rankings_\"]";
pub struct SeasonsParser {
selector_menu: Selector,
selector_name: Selector,
selector_link: Selector,
}
impl Default for SeasonsParser {
fn default() -> Self {
Self::new()
}
}
impl SeasonsParser {
pub fn new() -> Self {
SeasonsParser {
selector_menu: Selector::parse(SELECTOR_MENU).unwrap(),
selector_name: Selector::parse(SELECTOR_NAME).unwrap(),
selector_link: Selector::parse(SELECTOR_SEASON_LINK).unwrap(),
}
}
}
impl Parser for SeasonsParser {
type Output = Vec<Seasons>;
fn parse(&self, document: &str) -> Result<Self::Output> {
let document = Html::parse_document(document);
document
.select(&self.selector_menu)
.filter(|item| item.select(&self.selector_link).next().is_some())
.filter(|item| item.select(&self.selector_name).next().is_some())
.map(|item| {
let name = select_text(item, &self.selector_name)
.ok_or(ParseError::EmptyText {
role: "game mode name",
selector: SELECTOR_NAME,
})?
.trim_end_matches(" Menu");
let seasons = item
.select(&self.selector_link)
.map(|link| {
let text = link
.first_text()
.ok_or(ParseError::EmptyText {
role: "season name",
selector: SELECTOR_SEASON_LINK,
})?
.trim_end_matches(" Final Standings")
.trim_end_matches(" Final Rank")
.trim_end_matches(" Final Ranks");
let link = link.attr("href").ok_or(ParseError::EmptyText {
role: "season link",
selector: SELECTOR_SEASON_LINK,
})?;
let id = link
.trim_start_matches("rankings_")
.trim_end_matches(".cfm");
Ok(Season {
name: text.to_string(),
id: id.to_string(),
})
})
.collect::<Result<_>>()?;
Ok(Seasons {
mode: name.to_string(),
seasons,
})
})
.collect::<Result<Vec<_>>>()
}
}