remove_box #10

Merged
nyyu merged 3 commits from remove_box into master 2021-03-16 15:43:56 +01:00
5 changed files with 152 additions and 185 deletions

View file

@ -95,6 +95,8 @@ fn extract_json(pattern: &str, page: &str) -> String {
} }
impl DataSource for CGGDataSource { impl DataSource for CGGDataSource {
fn init(&self, _client: &ureq::Agent) {}
fn get_alias(&self) -> &str { fn get_alias(&self) -> &str {
"CGG" "CGG"
} }
@ -103,10 +105,10 @@ impl DataSource for CGGDataSource {
0 0
} }
fn get_champs_with_positions_and_patch( fn get_champs_with_positions(
&self, &self,
client: &ureq::Agent, client: &ureq::Agent,
) -> (IndexMap<String, Vec<String>>, String) { ) -> IndexMap<u32, Vec<String>> {
let req = client.get("https://champion.gg").call().unwrap(); let req = client.get("https://champion.gg").call().unwrap();
let page = &req.into_string().unwrap(); let page = &req.into_string().unwrap();
@ -122,11 +124,10 @@ impl DataSource for CGGDataSource {
.unwrap() .unwrap()
.insert(entry.0.to_owned(), entry.1.to_owned()); .insert(entry.0.to_owned(), entry.1.to_owned());
} }
let patch = datas.lol.champions_report[0].patch.to_owned();
let mut champions: IndexMap<String, Vec<String>> = IndexMap::new(); let mut champions: IndexMap<u32, Vec<String>> = IndexMap::new();
for champ in &datas.lol.champions_report { for champ in &datas.lol.champions_report {
let id = champ.champion_id.to_string(); let id = champ.champion_id;
if champions.contains_key(&id) { if champions.contains_key(&id) {
let mut roles = champions.get(&id).unwrap().to_owned(); let mut roles = champions.get(&id).unwrap().to_owned();
roles.push(champ.role.to_owned()); roles.push(champ.role.to_owned());
@ -140,7 +141,7 @@ impl DataSource for CGGDataSource {
CHAMPIONS_REPORT.lock().unwrap().push(report); CHAMPIONS_REPORT.lock().unwrap().push(report);
} }
(champions, patch) champions
} }
fn get_champ_data_with_win_pourcentage( fn get_champ_data_with_win_pourcentage(
@ -206,6 +207,7 @@ impl DataSource for CGGDataSource {
win_rate: stat.win_rate.unwrap(), win_rate: stat.win_rate.unwrap(),
games: stat.games.unwrap(), games: stat.games.unwrap(),
kda: stat.kda.unwrap(), kda: stat.kda.unwrap(),
patch: champ.patch.to_owned()
}, },
)); ));
} }

View file

@ -35,17 +35,20 @@ pub struct Stat {
pub win_rate: f64, pub win_rate: f64,
pub games: u32, pub games: u32,
pub kda: f64, pub kda: f64,
pub patch: String
} }
pub trait DataSource { pub trait DataSource {
fn init(&self, client: &ureq::Agent);
fn get_alias(&self) -> &str; fn get_alias(&self) -> &str;
fn get_timeout(&self) -> u64; fn get_timeout(&self) -> u64;
fn get_champs_with_positions_and_patch( fn get_champs_with_positions(
&self, &self,
client: &ureq::Agent, client: &ureq::Agent,
) -> (IndexMap<String, Vec<String>>, String); ) -> IndexMap<u32, Vec<String>>;
fn get_champ_data_with_win_pourcentage( fn get_champ_data_with_win_pourcentage(
&self, &self,
@ -58,7 +61,6 @@ pub trait DataSource {
&self, &self,
champ: &ChampInfo, champ: &ChampInfo,
positions: &[String], positions: &[String],
ver: &str,
path: &PathBuf, path: &PathBuf,
client: &ureq::Agent, client: &ureq::Agent,
) { ) {
@ -98,7 +100,7 @@ pub trait DataSource {
"{} {} {} - {:.2}% wins - {} games - {:.2} kda", "{} {} {} - {:.2}% wins - {} games - {:.2} kda",
self.get_alias(), self.get_alias(),
build.0, build.0,
ver, build.2.patch,
build.2.win_rate, build.2.win_rate,
build.2.games, build.2.games,
build.2.kda build.2.kda

View file

@ -3,13 +3,12 @@ use crate::data_source::{Build, DataSource, Item, Stat};
use crate::time::Duration; use crate::time::Duration;
use crate::ChampInfo; use crate::ChampInfo;
use indexmap::IndexMap; use indexmap::IndexMap;
use lazy_static::lazy_static;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use serde_json::{json, Value}; use serde_json::{json, Value};
use std::sync::Mutex;
pub struct KBDataSource { pub struct KBDataSource;
token: Option<String>,
internal_classname_mapping: IndexMap<String, String>,
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct ChampionResponse { struct ChampionResponse {
@ -74,8 +73,9 @@ struct KBBuild {
#[serde(rename = "skillOrder")] #[serde(rename = "skillOrder")]
skill_order: String, skill_order: String,
wins: f64, wins: f64,
games: f64, games: u32,
summoner: Summoner, summoner: Summoner,
patch: Patch,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -90,17 +90,11 @@ struct Summoner {
name: String, name: String,
} }
impl KBDataSource { lazy_static! {
pub fn new(client: &ureq::Agent) -> KBDataSource { static ref TOKEN: Mutex<Option<String>> = Mutex::new(None);
let mut datasource = KBDataSource { }
token: None,
internal_classname_mapping: IndexMap::new(),
};
datasource.token = datasource.get_auth_token(client);
datasource.internal_classname_mapping = datasource.get_classname_mapping(client);
datasource
}
impl KBDataSource {
// It will be better to use Result... // It will be better to use Result...
fn get_auth_token(&self, client: &ureq::Agent) -> Option<String> { fn get_auth_token(&self, client: &ureq::Agent) -> Option<String> {
let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() { let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() {
@ -126,33 +120,22 @@ impl KBDataSource {
} }
} }
fn get_classname_mapping(&self, client: &ureq::Agent) -> IndexMap<String, String> {
let mut mapping = IndexMap::new();
if let Some(data) = self.get_champion_response(client) {
for champ in data.champions {
mapping.insert(champ.classname, champ.name);
}
};
mapping
}
fn get_champion_response(&self, client: &ureq::Agent) -> Option<ChampionResponse> { fn get_champion_response(&self, client: &ureq::Agent) -> Option<ChampionResponse> {
let token = match self.token.clone() { if let Some(token) = TOKEN.lock().unwrap().as_ref() {
Some(t) => t, return match client
None => String::new(), .get("https://api.koreanbuilds.net/champions?patchid=-1")
}; .set("Accept", "application/json")
match client .set("Authorization", token.as_str())
.get("https://api.koreanbuilds.net/champions?patchid=-1") .call()
.set("Accept", "application/json") {
.set("Authorization", token.as_str()) Ok(resp) => match resp.into_json() {
.call() Ok(val) => val,
{ Err(_) => None,
Ok(resp) => match resp.into_json() { },
Ok(val) => val,
Err(_) => None, Err(_) => None,
}, };
Err(_) => None,
} }
None
} }
fn get_positions(position: Option<Position>) -> Vec<String> { fn get_positions(position: Option<Position>) -> Vec<String> {
@ -285,15 +268,22 @@ impl KBDataSource {
build.position.to_owned().to_uppercase(), build.position.to_owned().to_uppercase(),
blocks, blocks,
Stat { Stat {
win_rate: (build.wins / build.games) * 100., win_rate: (build.wins / build.games as f64) * 100.,
games: build.games as u32, games: build.games,
kda: 0.0, kda: 0.0,
patch: build.patch.patch_version.to_owned(),
}, },
) )
} }
} }
impl DataSource for KBDataSource { impl DataSource for KBDataSource {
fn init(&self, client: &ureq::Agent) {
if let Some(t) = self.get_auth_token(client) {
TOKEN.lock().unwrap().replace(t);
}
}
fn get_alias(&self) -> &str { fn get_alias(&self) -> &str {
"KB" "KB"
} }
@ -302,25 +292,18 @@ impl DataSource for KBDataSource {
300 300
} }
fn get_champs_with_positions_and_patch( fn get_champs_with_positions(&self, client: &ureq::Agent) -> IndexMap<u32, Vec<String>> {
&self,
client: &ureq::Agent,
) -> (IndexMap<String, Vec<String>>, String) {
let mut champions = IndexMap::new(); let mut champions = IndexMap::new();
let data: ChampionResponse = match self.get_champion_response(client) { let data: ChampionResponse = match self.get_champion_response(client) {
Some(val) => val, Some(val) => val,
None => { None => {
return (champions, String::new()); return champions;
} }
}; };
let patch = match data.patches.get(0) {
Some(p) => p.patch_version.clone(),
None => return (champions, String::new()),
};
for champ in data.champions { for champ in data.champions {
champions.insert(champ.classname, KBDataSource::get_positions(champ.builds)); champions.insert(champ.id, KBDataSource::get_positions(champ.builds));
} }
(champions, patch) champions
} }
fn get_champ_data_with_win_pourcentage( fn get_champ_data_with_win_pourcentage(
@ -330,45 +313,42 @@ impl DataSource for KBDataSource {
client: &ureq::Agent, client: &ureq::Agent,
) -> Vec<(String, Vec<Value>, Stat)> { ) -> Vec<(String, Vec<Value>, Stat)> {
let mut champ_data = vec![]; let mut champ_data = vec![];
if let Some(token) = self.token.clone() { if let Some(token) = TOKEN.lock().unwrap().as_ref() {
if let Some(map_id) = self.internal_classname_mapping.get(&champ.id) { let data: BuildResponse = match client
let data: BuildResponse = match client .get(&format!(
.get(&format!( "https://api.koreanbuilds.net/builds?chmpname={}&patchid=-2&position=COMPOSITE",
"https://api.koreanbuilds.net/builds?chmpname={}&patchid=-2&position=COMPOSITE", champ.id
map_id ))
)) .set("Accept", "application/json")
.set("Accept", "application/json") .set("Authorization", token.as_str())
.set("Authorization", token.as_str()) .call()
.call() {
{ Ok(resp) => match resp.into_json() {
Ok(resp) => match resp.into_json() { Ok(val) => val,
Ok(val) => val,
Err(_) => {
return vec![];
}
},
Err(_) => { Err(_) => {
return vec![]; return vec![];
} }
}; },
Err(_) => {
return vec![];
}
};
for pos in position { for pos in position {
let mut build: Option<&KBBuild> = None; let mut build: Option<&KBBuild> = None;
for b in &data.builds2 { for b in &data.builds2 {
if b.position.to_uppercase() == *pos { if b.position.to_uppercase() == *pos {
build = Some(b); build = Some(b);
break; break;
}
}
if let Some(b) = build {
champ_data.push(self.get_build(&b));
} }
} }
if let Some(b) = build {
champ_data.push(self.get_build(&b));
}
} }
}; }
champ_data champ_data
} }
} }
@ -379,10 +359,7 @@ mod tests {
#[test] #[test]
fn test_get_auth_token() { fn test_get_auth_token() {
let datasource = KBDataSource { let datasource = KBDataSource;
token: None,
internal_classname_mapping: IndexMap::new(),
};
let client = ureq::AgentBuilder::new() let client = ureq::AgentBuilder::new()
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.build(); .build();
@ -397,10 +374,11 @@ mod tests {
let client = ureq::AgentBuilder::new() let client = ureq::AgentBuilder::new()
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.build(); .build();
let datasource = KBDataSource::new(&client); let datasource = KBDataSource;
let champs_with_positions_and_patch = datasource.init(&client);
datasource.get_champs_with_positions_and_patch(&client); let champs_with_positions =
assert!(champs_with_positions_and_patch.0.len() > 0); datasource.get_champs_with_positions(&client);
assert!(champs_with_positions.len() > 0);
} }
#[test] #[test]
@ -408,15 +386,16 @@ mod tests {
let client = ureq::AgentBuilder::new() let client = ureq::AgentBuilder::new()
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.build(); .build();
let datasource = KBDataSource::new(&client); let datasource = KBDataSource;
datasource.init(&client);
let champ = ChampInfo { let champ = ChampInfo {
id: String::from("Aatrox"), id: String::from("Annie"),
name: String::from("Aatrox"), name: String::from("Annie"),
key: String::from("1"), key: String::from("1"),
}; };
let result = datasource.get_champ_data_with_win_pourcentage( let result = datasource.get_champ_data_with_win_pourcentage(
&champ, &champ,
&vec!["TOP".to_string()], &vec!["MID".to_string()],
&client, &client,
); );
assert!(!result.is_empty()); assert!(!result.is_empty());

View file

@ -1,5 +1,4 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use lazy_static::lazy_static;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use log::debug; use log::debug;
use log::{error, info, LevelFilter}; use log::{error, info, LevelFilter};
@ -75,62 +74,53 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.init()?; .init()?;
info!("CGG Item Sets"); info!("CGG Item Sets");
lazy_static! { let lol_champs_dir: PathBuf = match lol_champ_dir() {
static ref LOL_CHAMPS_DIR: PathBuf = match lol_champ_dir() { Ok(x) => x,
Ok(x) => x, Err(_e) => PathBuf::from(DEFAULT_LOL_CHAMPS_DIR),
Err(_e) => PathBuf::from(DEFAULT_LOL_CHAMPS_DIR), };
}; info!("LoL Champs Folder: {}", lol_champs_dir.to_str().unwrap());
static ref CLIENT: ureq::Agent = ureq::AgentBuilder::new()
.user_agent(USER_AGENT_VALUE)
.timeout(Duration::from_secs(10))
.build();
static ref REALM: Realm = CLIENT
.get("https://ddragon.leagueoflegends.com/realms/euw.json")
.call()
.unwrap()
.into_json()
.unwrap();
static ref CHAMPION: Champion = CLIENT
.get(&format!(
"https://ddragon.leagueoflegends.com/cdn/{}/data/en_US/champion.json",
REALM.v
))
.call()
.unwrap()
.into_json()
.unwrap();
static ref DATA_SOURCES: Vec<Box<dyn DataSource + Sync + Send>> = vec![
Box::new(PBDataSource),
Box::new(CGGDataSource),
Box::new(KBDataSource::new(&CLIENT)),
];
}
info!("LoL Champs Folder: {}", LOL_CHAMPS_DIR.to_str().unwrap()); let client: ureq::Agent = ureq::AgentBuilder::new()
info!("LoL version: {}", REALM.v); .user_agent(USER_AGENT_VALUE)
info!("LoL numbers of champs: {}", CHAMPION.data.len()); .timeout(Duration::from_secs(10))
.build();
let mut threads = vec![]; let realm: Realm = client
for data_source in DATA_SOURCES.iter() { .get("https://ddragon.leagueoflegends.com/realms/euw.json")
threads.push(thread::spawn(move || { .call()?
let init = Instant::now(); .into_json()?;
execute_data_source(&data_source, &CLIENT, &CHAMPION, &LOL_CHAMPS_DIR); info!("LoL version: {}", realm.v);
info!(
"{}: done in {} ms", let champion: Champion = client
data_source.get_alias(), .get(&format!(
init.elapsed().as_millis() "https://ddragon.leagueoflegends.com/cdn/{}/data/en_US/champion.json",
); realm.v
})); ))
} .call()?
for child in threads { .into_json()?;
let _ = child.join(); info!("LoL numbers of champs: {}", champion.data.len());
}
static DATA_SOURCES: [&'static (dyn DataSource + Sync + Send); 3] = [
&PBDataSource,
&CGGDataSource,
&KBDataSource,
];
DATA_SOURCES.par_iter().for_each(|data_source| {
let init = Instant::now();
execute_data_source(*data_source, &client, &champion, &lol_champs_dir);
info!(
"{}: done in {} ms",
data_source.get_alias(),
init.elapsed().as_millis()
);
});
Ok(()) Ok(())
} }
fn get_champ_from_key(champs: &Champion, key: &str) -> Option<String> { fn get_champ_from_key(champs: &Champion, key: u32) -> Option<String> {
for champ in champs.data.values() { for champ in champs.data.values() {
if key == champ.key { if key.to_string() == champ.key {
return Some(champ.id.to_owned()); return Some(champ.id.to_owned());
} }
} }
@ -138,14 +128,15 @@ fn get_champ_from_key(champs: &Champion, key: &str) -> Option<String> {
} }
fn execute_data_source( fn execute_data_source(
data_source: &Box<dyn DataSource + Sync + Send>, data_source: &(dyn DataSource + Sync + Send),
client: &ureq::Agent, client: &ureq::Agent,
champion: &Champion, champion: &Champion,
lol_champs_dir: &PathBuf, lol_champs_dir: &PathBuf,
) { ) {
let (champs, patch) = data_source.get_champs_with_positions_and_patch(&client); data_source.init(client);
let champs = data_source.get_champs_with_positions(&client);
info!("{} version: {}", data_source.get_alias(), patch);
info!( info!(
"{} numbers of champs: {}", "{} numbers of champs: {}",
data_source.get_alias(), data_source.get_alias(),
@ -159,8 +150,7 @@ fn execute_data_source(
client, client,
champion, champion,
lol_champs_dir, lol_champs_dir,
&patch, *id,
id,
positions, positions,
); );
}); });
@ -171,8 +161,7 @@ fn execute_data_source(
client, client,
champion, champion,
lol_champs_dir, lol_champs_dir,
&patch, *id,
id,
positions, positions,
); );
thread::sleep(Duration::from_millis(data_source.get_timeout())); thread::sleep(Duration::from_millis(data_source.get_timeout()));
@ -181,32 +170,25 @@ fn execute_data_source(
} }
fn get_and_write_item_set( fn get_and_write_item_set(
data_source: &Box<dyn DataSource + Sync + Send>, data_source: &(dyn DataSource + Sync + Send),
client: &ureq::Agent, client: &ureq::Agent,
champion: &Champion, champion: &Champion,
lol_champs_dir: &PathBuf, lol_champs_dir: &PathBuf,
patch: &str, id: u32,
id: &str,
positions: &[String], positions: &[String],
) { ) {
let mut champ_id: String = id.to_owned(); if let Some(champ_id) = get_champ_from_key(&champion, id) {
if let Some(champ) = champion.data.get(&champ_id) {
if id.parse::<u32>().is_ok() { if positions.is_empty() {
if let Some(c_id) = get_champ_from_key(&champion, &champ_id) { error!("{}: {} empty positions", data_source.get_alias(), &champ_id);
champ_id = c_id; } else {
} let path = lol_champs_dir.join(&champ_id).join("Recommended");
} fs::create_dir_all(&path).unwrap();
data_source.write_item_set(&champ, &positions, &path, &client);
if let Some(champ) = champion.data.get(&champ_id) { }
if positions.is_empty() {
error!("{}: {} empty positions", data_source.get_alias(), &champ_id);
} else { } else {
let path = lol_champs_dir.join(&champ_id).join("Recommended"); error!("{} not found in LoL champs", &champ_id);
fs::create_dir_all(&path).unwrap();
data_source.write_item_set(&champ, &positions, &patch, &path, &client);
} }
} else {
error!("{} not found in LoL champs", &champ_id);
} }
} }

View file

@ -6,6 +6,8 @@ use crate::ChampInfo;
pub struct PBDataSource; pub struct PBDataSource;
impl DataSource for PBDataSource { impl DataSource for PBDataSource {
fn init(&self, _client: &ureq::Agent) {}
fn get_alias(&self) -> &str { fn get_alias(&self) -> &str {
"PB" "PB"
} }
@ -14,11 +16,11 @@ impl DataSource for PBDataSource {
0 0
} }
fn get_champs_with_positions_and_patch( fn get_champs_with_positions(
&self, &self,
_client: &ureq::Agent, _client: &ureq::Agent,
) -> (IndexMap<String, Vec<String>>, String) { ) -> IndexMap<u32, Vec<String>> {
(IndexMap::new(), String::new()) IndexMap::new()
} }
fn get_champ_data_with_win_pourcentage( fn get_champ_data_with_win_pourcentage(