Improve dataset management UI
This commit is contained in:
parent
56bb676545
commit
21e0c62480
143
src/main.rs
143
src/main.rs
|
@ -117,13 +117,20 @@ 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");
|
||||||
|
|
||||||
let connection =
|
let connection =
|
||||||
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 1));
|
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
|
||||||
let datasets = list_datasets(&connection).expect("Error communicating with SQLite");
|
let datasets = list_datasets(&connection).expect("Error communicating with SQLite");
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for (name, metadata) in datasets {
|
for (name, metadata) in datasets {
|
||||||
|
if let Some(country) = metadata.country {
|
||||||
if let Some(state) = metadata.state {
|
if let Some(state) = metadata.state {
|
||||||
println!("{} - {}, {}", name, metadata.game_name, state);
|
println!(
|
||||||
|
"{} - {} (in {}, {})",
|
||||||
|
name, metadata.game_name, country, state
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("{} - {} (in {})", name, metadata.game_name, country);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("{} - {}", name, metadata.game_name);
|
println!("{} - {}", name, metadata.game_name);
|
||||||
}
|
}
|
||||||
|
@ -190,32 +197,136 @@ fn dataset_new(name: Option<String>, auth_token: Option<String>) {
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
|
|
||||||
print!("\nCountry to track ratings for (two-letter code, leave empty for none): ");
|
print!(
|
||||||
|
"
|
||||||
|
\x1b[4mCountry\x1b[0m
|
||||||
|
|
||||||
|
Enter the two-letter code for the country you want to track ratings in, e.g.
|
||||||
|
\"US\" for the United States. See \x1b[1m\x1b]8;;https://www.ups.com/worldshiphelp/\
|
||||||
|
WSA/ENU/AppHelp/mergedProjects/CORE/Codes/Country_Territory_and_Currency_Codes.htm\
|
||||||
|
\x1b\\this site\x1b]8;;\x1b\\\x1b[0m for a list of these codes.
|
||||||
|
If no code is entered, then the dataset will track all players globally.
|
||||||
|
|
||||||
|
Country to track ratings for (leave empty for none): "
|
||||||
|
);
|
||||||
let country = {
|
let country = {
|
||||||
let string = read_string();
|
let mut string = read_string();
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else if string.len() == 2 && string.chars().all(|c| c.is_ascii_alphabetic()) {
|
||||||
|
string.make_ascii_uppercase();
|
||||||
Some(string)
|
Some(string)
|
||||||
|
} else {
|
||||||
|
error("Input is not a two-letter code", 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = if country.as_ref().is_some_and(|s| s == "US" || s == "CA") {
|
let state = if country.as_ref().is_some_and(|s| s == "US" || s == "CA") {
|
||||||
print!("\nState/province to track ratings for (two-letter code, leave empty for none): ");
|
print!(
|
||||||
let string = read_string();
|
"
|
||||||
|
\x1b[4mState/Province\x1b[0m
|
||||||
|
|
||||||
|
Enter the two-letter code for the US state or Canadian province you want to track
|
||||||
|
ratings in, e.g. \"CA\" for California. See \x1b[1m\x1b]8;;https://www.ups.com/worldshiphelp/\
|
||||||
|
WSA/ENU/AppHelp/mergedProjects/CORE/Codes/State_Province_Codes.htm\x1b\\this site\
|
||||||
|
\x1b]8;;\x1b\\\x1b[0m for a list of these codes.
|
||||||
|
If no code is entered, then the dataset will track all players within the country.
|
||||||
|
|
||||||
|
State/province to track ratings for (leave empty for none): "
|
||||||
|
);
|
||||||
|
let mut string = read_string();
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else if string.len() == 2 && string.chars().all(|c| c.is_ascii_alphabetic()) {
|
||||||
|
string.make_ascii_uppercase();
|
||||||
Some(string)
|
Some(string)
|
||||||
|
} else {
|
||||||
|
error("Input is not a two-letter code", 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Advanced Options
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
let mut decay_rate = 0.5;
|
||||||
|
let mut period_days = 30.0;
|
||||||
|
let mut tau = 0.2;
|
||||||
|
|
||||||
|
print!("\nConfigure advanced options? (y/n) ");
|
||||||
|
if let Some('y') = read_string().chars().next() {
|
||||||
|
// Decay Rate
|
||||||
|
|
||||||
|
print!(
|
||||||
|
"
|
||||||
|
\x1b[4mNetwork Decay Rate\x1b[0m
|
||||||
|
|
||||||
|
The network decay rate is a number between 0 and 1 that controls how the
|
||||||
|
advantage network reacts to player wins and losses. If the decay rate is 1,
|
||||||
|
then it is assumed that a player's skill against one opponent always carries
|
||||||
|
over to all other opponents. If the decay rate is 0, then all player match-ups
|
||||||
|
are assumed to be independent of each other.
|
||||||
|
|
||||||
|
Network decay rate (default 0.5): "
|
||||||
|
);
|
||||||
|
let decay_rate_input = read_string();
|
||||||
|
if !decay_rate_input.is_empty() {
|
||||||
|
decay_rate = decay_rate_input
|
||||||
|
.parse::<f64>()
|
||||||
|
.unwrap_or_else(|_| error("Not a number", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating Period
|
||||||
|
|
||||||
|
print!(
|
||||||
|
"
|
||||||
|
\x1b[4mRating Period\x1b[0m
|
||||||
|
|
||||||
|
The rating period is an interval of time that dictates how player ratings change
|
||||||
|
during inactivity. Ideally the rating period should be somewhat long, long
|
||||||
|
enough to expect almost every player in the dataset to have played at least a
|
||||||
|
few sets.
|
||||||
|
|
||||||
|
Rating period (in days, default 30): "
|
||||||
|
);
|
||||||
|
let period_input = read_string();
|
||||||
|
if !period_input.is_empty() {
|
||||||
|
period_days = period_input
|
||||||
|
.parse::<f64>()
|
||||||
|
.unwrap_or_else(|_| error("Not a number", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tau coefficient
|
||||||
|
|
||||||
|
print!(
|
||||||
|
"
|
||||||
|
\x1b[4mTau Constant\x1b[0m
|
||||||
|
|
||||||
|
The tau constant is an internal system constant that roughly represents how
|
||||||
|
much random chance and luck play a role in game outcomes. In games where match
|
||||||
|
results are highly predictable, and a player's skill is the sole factor for
|
||||||
|
whether they will win, the tau constant should be high (0.9 - 1.2). In games
|
||||||
|
where luck matters, and more improbable victories can occur, the tau constant
|
||||||
|
should be low (0.2 - 0.4).
|
||||||
|
|
||||||
|
The tau constant is set low by default, since skill-based competitive video
|
||||||
|
games tend to be on the more luck-heavy side.
|
||||||
|
|
||||||
|
Tau constant (default 0.2): "
|
||||||
|
);
|
||||||
|
let tau_input = read_string();
|
||||||
|
if !tau_input.is_empty() {
|
||||||
|
tau = tau_input
|
||||||
|
.parse::<f64>()
|
||||||
|
.unwrap_or_else(|_| error("Not a number", 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Done configuring
|
// Done configuring
|
||||||
|
|
||||||
let connection =
|
let connection =
|
||||||
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 1));
|
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
|
||||||
new_dataset(
|
new_dataset(
|
||||||
&connection,
|
&connection,
|
||||||
&name,
|
&name,
|
||||||
|
@ -225,12 +336,14 @@ fn dataset_new(name: Option<String>, auth_token: Option<String>) {
|
||||||
game_name,
|
game_name,
|
||||||
country,
|
country,
|
||||||
state,
|
state,
|
||||||
decay_rate: 0.5,
|
decay_rate,
|
||||||
period: (3600 * 24 * 30) as f64,
|
period: (3600 * 24) as f64 * period_days,
|
||||||
tau: 0.2,
|
tau,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("Error communicating with SQLite");
|
.expect("Error communicating with SQLite");
|
||||||
|
|
||||||
|
println!("\nCreated dataset {}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dataset_delete(name: Option<String>) {
|
fn dataset_delete(name: Option<String>) {
|
||||||
|
@ -242,8 +355,8 @@ fn dataset_delete(name: Option<String>) {
|
||||||
});
|
});
|
||||||
|
|
||||||
let connection =
|
let connection =
|
||||||
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 1));
|
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
|
||||||
delete_dataset(&connection, &name).expect("Error communicating with SQLite");
|
delete_dataset(&connection, &name).unwrap_or_else(|_| error("That dataset does not exist!", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) {
|
fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) {
|
||||||
|
@ -254,7 +367,7 @@ fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) {
|
||||||
.unwrap_or_else(|| error("Access token not provided", 1));
|
.unwrap_or_else(|| error("Access token not provided", 1));
|
||||||
|
|
||||||
let connection =
|
let connection =
|
||||||
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 1));
|
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
|
||||||
|
|
||||||
let all_datasets = list_dataset_names(&connection).unwrap();
|
let all_datasets = list_dataset_names(&connection).unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue