Implement basic logic for syncing

This commit is contained in:
Kiana Sheibani 2023-09-30 01:43:33 -04:00
parent 3ed8c6ad71
commit cd98f0cd26
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
4 changed files with 101 additions and 21 deletions

View file

@ -4,8 +4,8 @@ use std::fs::{self, OpenOptions};
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Return the default path to the datasets file. /// Return the path to the datasets file.
fn default_datasets_path(config_dir: &Path) -> io::Result<PathBuf> { fn datasets_path(config_dir: &Path) -> io::Result<PathBuf> {
let mut path = config_dir.to_owned(); let mut path = config_dir.to_owned();
path.push("ggelo"); path.push("ggelo");
@ -21,11 +21,12 @@ fn default_datasets_path(config_dir: &Path) -> io::Result<PathBuf> {
} }
pub fn open_datasets(config_dir: &Path) -> sqlite::Result<Connection> { pub fn open_datasets(config_dir: &Path) -> sqlite::Result<Connection> {
let path = default_datasets_path(config_dir).unwrap(); let path = datasets_path(config_dir).unwrap();
let query = " let query = "
CREATE TABLE IF NOT EXISTS datasets ( CREATE TABLE IF NOT EXISTS datasets (
name TEXT UNIQUE NOT NULL name TEXT UNIQUE NOT NULL,
last_sync INTEGER NOT NULL DEFAULT 1
) STRICT;"; ) STRICT;";
let connection = sqlite::open(path)?; let connection = sqlite::open(path)?;
@ -71,6 +72,29 @@ pub fn new_dataset(connection: &Connection, dataset: &str) -> sqlite::Result<()>
connection.execute(query) connection.execute(query)
} }
pub fn get_last_sync(connection: &Connection, dataset: &str) -> sqlite::Result<Option<u64>> {
let query = "SELECT last_sync FROM datasets WHERE name = ?";
Ok(connection
.prepare(query)?
.into_iter()
.bind((1, dataset))?
.map(|x| x.map(|r| r.read::<i64, _>("last_sync").to_owned() as u64))
.next()
.and_then(Result::ok))
}
pub fn update_last_sync(connection: &Connection, dataset: &str, sync: u64) -> sqlite::Result<()> {
let query = "UPDATE datasets SET last_sync = :sync WHERE name = :dataset";
connection
.prepare(query)?
.into_iter()
.bind((":sync", sync as i64))?
.bind((":dataset", dataset))?
.try_for_each(|x| x.map(|_| ()))
}
// Score calculation // Score calculation
/// Calculate the collective expected score for each team. /// Calculate the collective expected score for each team.
@ -163,11 +187,7 @@ pub fn update_ratings(
}) })
} }
pub fn update_from_set( fn update_from_set(connection: &Connection, dataset: &str, results: SetData) -> sqlite::Result<()> {
connection: &Connection,
dataset: &str,
results: SetData,
) -> sqlite::Result<()> {
let players_data = results.teams; let players_data = results.teams;
add_players(connection, dataset, &players_data)?; add_players(connection, dataset, &players_data)?;
@ -180,3 +200,24 @@ pub fn update_from_set(
); );
update_ratings(connection, dataset, elos) update_ratings(connection, dataset, elos)
} }
fn update_from_tournament(
connection: &Connection,
dataset: &str,
results: TournamentData,
) -> sqlite::Result<()> {
results
.sets
.into_iter()
.try_for_each(|set| update_from_set(connection, dataset, set))
}
pub fn update_from_tournaments(
connection: &Connection,
dataset: &str,
results: Vec<TournamentData>,
) -> sqlite::Result<()> {
results
.into_iter()
.try_for_each(|tour| update_from_tournament(connection, dataset, tour))
}

View file

@ -2,12 +2,11 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::time::SystemTime;
mod queries; mod queries;
use queries::*; use queries::*;
mod state;
use state::*;
mod datasets; mod datasets;
use datasets::*; use datasets::*;
@ -35,6 +34,12 @@ enum Subcommands {
#[command(subcommand)] #[command(subcommand)]
subcommand: DatasetSC, subcommand: DatasetSC,
}, },
Sync {
#[arg(group = "datasets")]
names: Vec<String>,
#[arg(short, long, group = "datasets")]
all: bool,
},
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@ -57,6 +62,8 @@ fn main() {
Subcommands::Dataset { Subcommands::Dataset {
subcommand: DatasetSC::Delete { name }, subcommand: DatasetSC::Delete { name },
} => dataset_delete(name), } => dataset_delete(name),
Subcommands::Sync { names, all } => sync(names, all, cli.auth_token),
} }
} }
@ -102,3 +109,41 @@ fn dataset_delete(name: Option<String>) {
let connection = open_datasets(&config_dir).unwrap(); let connection = open_datasets(&config_dir).unwrap();
delete_dataset(&connection, &name).unwrap(); delete_dataset(&connection, &name).unwrap();
} }
fn sync(names: Vec<String>, all: bool, auth_token: Option<String>) {
let config_dir = dirs::config_dir().unwrap();
let auth = auth_token.or_else(|| get_auth_token(&config_dir)).unwrap();
let connection = open_datasets(&config_dir).unwrap();
let names = if all {
list_datasets(&connection).unwrap()
} else {
names
};
for name in names {
let last_sync = get_last_sync(&connection, &name).unwrap().unwrap();
let results = run_query::<TournamentSets, _>(
TournamentSetsVars {
last_query: Timestamp(last_sync),
game_id: VideogameId(1),
country: None,
state: None,
},
&auth,
)
.unwrap();
update_from_tournaments(&connection, &name, results).unwrap();
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
update_last_sync(&connection, &name, current_time).unwrap();
}
}

View file

@ -14,7 +14,7 @@ use schema::schema;
// Auth key // Auth key
pub fn get_auth_key(config_dir: &Path) -> Option<String> { pub fn get_auth_token(config_dir: &Path) -> Option<String> {
use std::env::{var, VarError}; use std::env::{var, VarError};
use std::fs::read_to_string; use std::fs::read_to_string;
@ -66,7 +66,7 @@ pub trait QueryUnwrap<Vars>: 'static + QueryBuilder<Vars> {
} }
// Generic function for running start.gg queries // Generic function for running start.gg queries
pub fn run_query<Builder, Vars>(vars: Vars, state: &AppState) -> Option<Builder::Unwrapped> pub fn run_query<Builder, Vars>(vars: Vars, auth_token: &str) -> Option<Builder::Unwrapped>
where where
Builder: QueryUnwrap<Vars>, Builder: QueryUnwrap<Vars>,
Vars: Serialize, Vars: Serialize,
@ -78,7 +78,7 @@ where
let response = reqwest::blocking::Client::new() let response = reqwest::blocking::Client::new()
.post("https://api.start.gg/gql/alpha") .post("https://api.start.gg/gql/alpha")
.header("Authorization", String::from("Bearer ") + &state.auth_token) .header("Authorization", String::from("Bearer ") + auth_token)
.run_graphql(query); .run_graphql(query);
Builder::unwrap_response(response.unwrap()) Builder::unwrap_response(response.unwrap())

View file

@ -1,6 +0,0 @@
use std::path::PathBuf;
pub struct AppState {
pub config_dir: PathBuf,
pub auth_token: String,
}