Implement basic player info command

This commit is contained in:
Kiana Sheibani 2023-11-26 14:25:14 -05:00
parent 3836ccfb9f
commit c06c18c0ba
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
4 changed files with 123 additions and 20 deletions

View file

@ -126,6 +126,7 @@ pub fn new_dataset(
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT, name TEXT,
prefix TEXT, prefix TEXT,
slug TEXT NOT NULL,
last_played INTEGER NOT NULL, last_played INTEGER NOT NULL,
deviation REAL NOT NULL, deviation REAL NOT NULL,
volatility REAL NOT NULL, volatility REAL NOT NULL,
@ -248,20 +249,49 @@ pub fn add_players(
) -> sqlite::Result<()> { ) -> sqlite::Result<()> {
let query = format!( let query = format!(
r#"INSERT OR IGNORE INTO "{}_players" r#"INSERT OR IGNORE INTO "{}_players"
(id, name, prefix, last_played, deviation, volatility, sets_won, sets_lost) (id, name, prefix, slug, last_played, deviation, volatility, sets_won, sets_lost)
VALUES (?, ?, ?, ?, 2.01, 0.06, '', '')"#, VALUES (?, ?, ?, ?, ?, 2.01, 0.06, '', '')"#,
dataset dataset
); );
teams.iter().try_for_each(|team| { teams.iter().try_for_each(|team| {
team.iter().try_for_each(|PlayerData { id, name, prefix }| { team.iter().try_for_each(
|PlayerData {
id,
name,
prefix,
slug,
}| {
let mut statement = connection.prepare(&query)?; let mut statement = connection.prepare(&query)?;
statement.bind((1, id.0 as i64))?; statement.bind((1, id.0 as i64))?;
statement.bind((2, name.as_ref().map(|x| &x[..])))?; statement.bind((2, &name[..]))?;
statement.bind((3, prefix.as_ref().map(|x| &x[..])))?; statement.bind((3, prefix.as_ref().map(|x| &x[..])))?;
statement.bind((4, time.0 as i64))?; statement.bind((4, &slug[..]))?;
statement.bind((5, time.0 as i64))?;
statement.into_iter().try_for_each(|x| x.map(|_| ())) statement.into_iter().try_for_each(|x| x.map(|_| ()))
},
)
}) })
}
pub fn get_player(
connection: &Connection,
dataset: &str,
player: PlayerId,
) -> sqlite::Result<PlayerData> {
let query = format!(
r#"SELECT name, prefix, slug FROM "{}_players" WHERE id = ?"#,
dataset
);
let mut statement = connection.prepare(&query)?;
statement.bind((1, player.0 as i64))?;
statement.next()?;
Ok(PlayerData {
id: player,
name: statement.read::<String, _>("name")?,
prefix: statement.read::<Option<String>, _>("prefix")?,
slug: statement.read::<String, _>("slug")?,
}) })
} }
@ -599,8 +629,9 @@ CREATE TABLE IF NOT EXISTS datasets (
(1..=num) (1..=num)
.map(|i| PlayerData { .map(|i| PlayerData {
id: PlayerId(i), id: PlayerId(i),
name: Some(format!("{}", i)), name: format!("{}", i),
prefix: None, prefix: None,
slug: String::from("a"),
}) })
.collect() .collect()
} }

View file

@ -3,7 +3,6 @@
#![feature(extend_one)] #![feature(extend_one)]
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::io::{self, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use time_format::strftime_utc; use time_format::strftime_utc;
@ -23,7 +22,7 @@ use util::*;
#[command(name = "StartRNR")] #[command(name = "StartRNR")]
#[command(author = "Kiana Sheibani <kiana.a.sheibani@gmail.com>")] #[command(author = "Kiana Sheibani <kiana.a.sheibani@gmail.com>")]
#[command(version = "0.2.0")] #[command(version = "0.2.0")]
#[command(about = "StartRNR - Elo rating calculator for start.gg tournaments", long_about = None)] #[command(about = "StartRNR - Rating system for video games based on start.gg", long_about = None)]
struct Cli { struct Cli {
#[command(subcommand)] #[command(subcommand)]
subcommand: Subcommands, subcommand: Subcommands,
@ -63,8 +62,8 @@ enum Subcommands {
#[command( #[command(
about = "Sync player ratings", about = "Sync player ratings",
long_about = "Pull recent tournament data off of start.gg and use it to long_about = "Pull recent tournament data off of start.gg and use it to
update the network. This command will automatically keep track of the last time each update player ratings. This command will automatically keep track of the last time
dataset was synced to ensure that each tournament is only accounted for once." each dataset was synced to ensure that each tournament is only accounted for once."
)] )]
Sync { Sync {
#[arg( #[arg(
@ -77,6 +76,19 @@ created if it does not already exist."
#[arg(short, long, help = "Sync all stored databases")] #[arg(short, long, help = "Sync all stored databases")]
all: bool, all: bool,
}, },
#[command(about = "Access player information")]
Player {
#[command(subcommand)]
subcommand: PlayerSC,
#[arg(
short,
long,
value_name = "DATASET",
global = true,
help = "The dataset to access"
)]
dataset: Option<String>,
},
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@ -89,9 +101,16 @@ enum DatasetSC {
Delete { name: Option<String> }, Delete { name: Option<String> },
} }
#[derive(Subcommand)]
enum PlayerSC {
#[command(about = "Get info of player")]
Info { player: String },
}
fn main() { fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
#[allow(unreachable_patterns)]
match cli.subcommand { match cli.subcommand {
Subcommands::Dataset { Subcommands::Dataset {
subcommand: DatasetSC::List, subcommand: DatasetSC::List,
@ -103,10 +122,19 @@ fn main() {
subcommand: DatasetSC::Delete { name }, subcommand: DatasetSC::Delete { name },
} => dataset_delete(name), } => dataset_delete(name),
Subcommands::Player {
subcommand: PlayerSC::Info { player },
dataset,
} => player_info(dataset, player),
Subcommands::Sync { datasets, all } => sync(datasets, all, cli.auth_token), Subcommands::Sync { datasets, all } => sync(datasets, all, cli.auth_token),
_ => println!("This feature is currently unimplemented."),
} }
} }
// Datasets
fn dataset_list() { fn dataset_list() {
let config_dir = dirs::config_dir().expect("Could not determine config directory"); let config_dir = dirs::config_dir().expect("Could not determine config directory");
@ -431,6 +459,35 @@ fn dataset_delete(name: Option<String>) {
delete_dataset(&connection, &name).unwrap_or_else(|_| error("That dataset does not exist!", 1)); delete_dataset(&connection, &name).unwrap_or_else(|_| error("That dataset does not exist!", 1));
} }
// Players
fn player_info(dataset: Option<String>, player: String) {
let config_dir = dirs::config_dir().expect("Could not determine config directory");
let dataset = dataset.unwrap_or_else(|| String::from("default"));
let connection =
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
let player_id = PlayerId(player.parse::<u64>().unwrap());
let PlayerData {
id,
name,
prefix,
slug,
} = get_player(&connection, &dataset, player_id).unwrap();
let (deviation, volatility, last_played) =
get_player_data(&connection, &dataset, player_id).unwrap();
print!("\n\x1b]8;;https://www.start.gg/{}\x1b\\", slug);
if let Some(pre) = prefix {
print!("\x1b[2m{}\x1b[0m ", pre);
}
println!("\x1b[1m{}\x1b[0m\x1b]8;;\x1b\\ ({})", name, id.0);
}
// Sync
fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) { fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) {
let config_dir = dirs::config_dir().unwrap(); let config_dir = dirs::config_dir().unwrap();

View file

@ -25,7 +25,7 @@ pub struct EventSets {
#[derive(cynic::QueryFragment, Debug)] #[derive(cynic::QueryFragment, Debug)]
#[cynic(variables = "EventSetsVars")] #[cynic(variables = "EventSetsVars")]
struct Event { struct Event {
#[arguments(page: $page, perPage: 30, sortType: RECENT)] #[arguments(page: $page, perPage: 25, sortType: RECENT)]
sets: Option<SetConnection>, sets: Option<SetConnection>,
} }
@ -74,6 +74,12 @@ struct Player {
id: Option<PlayerId>, id: Option<PlayerId>,
gamer_tag: Option<String>, gamer_tag: Option<String>,
prefix: Option<String>, prefix: Option<String>,
user: Option<User>,
}
#[derive(cynic::QueryFragment, Debug)]
struct User {
slug: Option<String>,
} }
// Unwrap // Unwrap
@ -121,8 +127,9 @@ impl QueryUnwrap<EventSetsVars> for EventSets {
let p_ = p.player?; let p_ = p.player?;
Some(PlayerData { Some(PlayerData {
id: p_.id?, id: p_.id?,
name: p_.gamer_tag, name: p_.gamer_tag?,
prefix: p_.prefix, prefix: p_.prefix.filter(|pr| !pr.is_empty()),
slug: p_.user?.slug?,
}) })
}) })
.try_collect() .try_collect()

View file

@ -23,6 +23,12 @@ struct Player {
id: Option<PlayerId>, id: Option<PlayerId>,
gamer_tag: Option<String>, gamer_tag: Option<String>,
prefix: Option<String>, prefix: Option<String>,
user: Option<User>,
}
#[derive(cynic::QueryFragment, Debug)]
struct User {
slug: Option<String>,
} }
// Unwrapping // Unwrapping
@ -30,8 +36,9 @@ struct Player {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PlayerData { pub struct PlayerData {
pub id: PlayerId, pub id: PlayerId,
pub name: Option<String>, pub name: String,
pub prefix: Option<String>, pub prefix: Option<String>,
pub slug: String,
} }
impl QueryUnwrap<PlayerInfoVars> for PlayerInfo { impl QueryUnwrap<PlayerInfoVars> for PlayerInfo {
@ -41,8 +48,9 @@ impl QueryUnwrap<PlayerInfoVars> for PlayerInfo {
let player = response.data?.player?; let player = response.data?.player?;
Some(PlayerData { Some(PlayerData {
id: player.id?, id: player.id?,
name: player.gamer_tag, name: player.gamer_tag?,
prefix: player.prefix.filter(|pr| !pr.is_empty()), prefix: player.prefix.filter(|pr| !pr.is_empty()),
slug: player.user?.slug?,
}) })
} }
} }