Skip to content

Commit

Permalink
Start tracking game stats and sending them to uwhportal (#225)
Browse files Browse the repository at this point in the history
* Refactor to use BlackWhiteBundles internally

* Split TournamentManager and add GameStats

* Start sending game stats to uwhportal
  • Loading branch information
TristanDebrunner authored Dec 7, 2023
1 parent b56e5fa commit 79d99f8
Show file tree
Hide file tree
Showing 14 changed files with 1,569 additions and 1,092 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions refbox/src/app/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::tournament_manager::PenaltyKind;
use crate::tournament_manager::penalty::PenaltyKind;
use tokio::time::Duration;
use uwh_common::{
game_snapshot::{Color as GameColor, GameSnapshot},
Expand Down Expand Up @@ -65,8 +65,7 @@ pub enum Message {
GotRemoteId(u32),
DeleteRemote(usize),
ConfirmationSelected(ConfirmationOption),
BlackTimeout(bool),
WhiteTimeout(bool),
TeamTimeout(GameColor, bool),
RefTimeout(bool),
PenaltyShot(bool),
EndTimeout,
Expand Down Expand Up @@ -125,8 +124,7 @@ impl Message {
| Self::GotRemoteId(_)
| Self::DeleteRemote(_)
| Self::ConfirmationSelected(_)
| Self::BlackTimeout(_)
| Self::WhiteTimeout(_)
| Self::TeamTimeout(_, _)
| Self::RefTimeout(_)
| Self::PenaltyShot(_)
| Self::EndTimeout
Expand Down
102 changes: 60 additions & 42 deletions refbox/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
config::{Config, Mode},
penalty_editor::*,
sound_controller::*,
tournament_manager::*,
tournament_manager::{penalty::*, *},
};
use iced::{executor, widget::column, Application, Command, Subscription};
use iced_futures::{
Expand All @@ -29,7 +29,8 @@ use tokio_serial::SerialPortBuilder;
use uwh_common::{
config::Game as GameConfig,
drawing_support::*,
game_snapshot::{Color as GameColor, GamePeriod, GameSnapshot, TimeoutSnapshot},
game_snapshot::{GamePeriod, GameSnapshot, TimeoutSnapshot},
uwhportal::UwhPortalClient,
uwhscores::*,
};

Expand Down Expand Up @@ -64,6 +65,7 @@ pub struct RefBoxApp {
message_listener: MessageListener,
msg_tx: mpsc::UnboundedSender<Message>,
client: Option<Client>,
uwhportal_client: Option<UwhPortalClient>,
using_uwhscores: bool,
tournaments: Option<BTreeMap<u32, TournamentInfo>>,
games: Option<BTreeMap<u32, GameInfo>>,
Expand Down Expand Up @@ -117,7 +119,7 @@ impl RefBoxApp {
fn apply_snapshot(&mut self, mut new_snapshot: GameSnapshot) {
if new_snapshot.current_period != self.snapshot.current_period {
if new_snapshot.current_period == GamePeriod::BetweenGames {
self.handle_game_end(new_snapshot.next_game_number);
self.handle_game_end(new_snapshot.game_number, new_snapshot.next_game_number);
} else if self.snapshot.current_period == GamePeriod::BetweenGames {
self.handle_game_start(new_snapshot.game_number);
}
Expand Down Expand Up @@ -387,6 +389,18 @@ impl RefBoxApp {
}
}

fn post_game_stats(&self, tid: u32, gid: u32, stats: String) {
if let Some(ref uwhportal_client) = self.uwhportal_client {
let request = uwhportal_client.post_game_stats(tid, gid, stats);
tokio::spawn(async move {
match request.await {
Ok(()) => info!("Successfully posted game stats"),
Err(e) => error!("Failed to post game stats: {e}"),
}
});
}
}

fn handle_game_start(&mut self, new_game_num: u32) {
if self.using_uwhscores {
if let (Some(ref games), Some(ref pool)) = (&self.games, &self.current_pool) {
Expand Down Expand Up @@ -420,12 +434,28 @@ impl RefBoxApp {
}
}

fn handle_game_end(&self, next_game_num: u32) {
fn handle_game_end(&self, game_number: u32, next_game_num: u32) {
if self.using_uwhscores {
let mut stats = self
.tm
.lock()
.unwrap()
.last_game_stats()
.map(|s| s.as_json());

if let Some(ref stats) = stats {
info!("Game ended, stats were: {:?}", stats);
} else {
warn!("Game ended, but no stats were available");
}

if let Some(tid) = self.current_tid {
self.request_game_details(tid, next_game_num);
if let Some(stats) = stats.take() {
self.post_game_stats(tid, game_number, stats);
}
} else {
error!("Missing current tid to request game info");
error!("Missing current tid to handle game end");
}
}
}
Expand Down Expand Up @@ -480,6 +510,20 @@ impl Application for RefBoxApp {
}
};

let uwhportal_client = match UwhPortalClient::new(
&config.uwhportal.url,
Some(&config.uwhportal.email),
Some(&config.uwhportal.password),
require_https,
REQUEST_TIMEOUT,
) {
Ok(c) => Some(c),
Err(e) => {
error!("Failed to start UWH Portal Client: {e}");
None
}
};

let clock_running_receiver = tm.get_start_stop_rx();

let tm = Arc::new(Mutex::new(tm));
Expand Down Expand Up @@ -510,6 +554,7 @@ impl Application for RefBoxApp {
message_listener,
msg_tx,
client,
uwhportal_client,
using_uwhscores: false,
tournaments: None,
games: None,
Expand Down Expand Up @@ -649,10 +694,7 @@ impl Application for RefBoxApp {
Message::EditScores => {
let tm = self.tm.lock().unwrap();
self.app_state = AppState::ScoreEdit {
scores: BlackWhiteBundle {
black: tm.get_b_score(),
white: tm.get_w_score(),
},
scores: tm.get_scores(),
is_confirmation: false,
};
trace!("AppState changed to {:?}", self.app_state);
Expand All @@ -664,10 +706,7 @@ impl Application for RefBoxApp {
} else {
let mut tm = self.tm.lock().unwrap();
let now = Instant::now();
match color {
GameColor::Black => tm.add_b_score(0, now),
GameColor::White => tm.add_w_score(0, now),
}
tm.add_score(color, 0, now);
let snapshot = tm.generate_snapshot(now).unwrap(); // TODO: Remove this unwrap
std::mem::drop(tm);
self.apply_snapshot(snapshot);
Expand Down Expand Up @@ -704,7 +743,7 @@ impl Application for RefBoxApp {
self.post_game_score(game, scores);
}

tm.set_scores(scores.black, scores.white, now);
tm.set_scores(scores, now);
tm.start_clock(now);

// Update `tm` after game ends to get into Between Games
Expand All @@ -718,7 +757,7 @@ impl Application for RefBoxApp {
tm.stop_clock(now).unwrap();
AppState::ConfirmScores(scores)
} else {
tm.set_scores(scores.black, scores.white, now);
tm.set_scores(scores, now);
AppState::MainPage
}
} else {
Expand Down Expand Up @@ -897,18 +936,12 @@ impl Application for RefBoxApp {

let app_state = if tm.current_period() == GamePeriod::SuddenDeath {
tm.stop_clock(now).unwrap();
let mut scores = BlackWhiteBundle {
black: tm.get_b_score(),
white: tm.get_w_score(),
};
let mut scores = tm.get_scores();
scores[color] = scores[color].saturating_add(1);

AppState::ConfirmScores(scores)
} else {
match color {
GameColor::Black => tm.add_b_score(player.try_into().unwrap(), now),
GameColor::White => tm.add_w_score(player.try_into().unwrap(), now),
};
tm.add_score(color, player.try_into().unwrap(), now);
AppState::MainPage
};
let snapshot = tm.generate_snapshot(now).unwrap();
Expand Down Expand Up @@ -1467,7 +1500,7 @@ impl Application for RefBoxApp {
self.post_game_score(game, scores);
}

tm.set_scores(scores.black, scores.white, now);
tm.set_scores(scores, now);
tm.start_clock(now);
tm.update(now + Duration::from_millis(2)).unwrap(); // Need to update after game ends

Expand All @@ -1484,28 +1517,13 @@ impl Application for RefBoxApp {

trace!("AppState changed to {:?}", self.app_state);
}
Message::BlackTimeout(switch) => {
let mut tm = self.tm.lock().unwrap();
let now = Instant::now();
if switch {
tm.switch_to_b_timeout().unwrap();
} else {
tm.start_b_timeout(now).unwrap();
}
if let AppState::TimeEdit(_, _, ref mut time) = self.app_state {
*time = Some(tm.timeout_clock_time(now).unwrap());
}
let snapshot = tm.generate_snapshot(now).unwrap();
std::mem::drop(tm);
self.apply_snapshot(snapshot);
}
Message::WhiteTimeout(switch) => {
Message::TeamTimeout(color, switch) => {
let mut tm = self.tm.lock().unwrap();
let now = Instant::now();
if switch {
tm.switch_to_w_timeout().unwrap();
tm.switch_to_team_timeout(color).unwrap();
} else {
tm.start_w_timeout(now).unwrap();
tm.start_team_timeout(color, now).unwrap();
}
if let AppState::TimeEdit(_, _, ref mut time) = self.app_state {
*time = Some(tm.timeout_clock_time(now).unwrap());
Expand Down
20 changes: 11 additions & 9 deletions refbox/src/app/view_builders/shared_elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use std::{
use uwh_common::{
config::Game as GameConfig,
drawing_support::*,
game_snapshot::{GamePeriod, GameSnapshot, PenaltySnapshot, PenaltyTime, TimeoutSnapshot},
game_snapshot::{
Color as GameColor, GamePeriod, GameSnapshot, PenaltySnapshot, PenaltyTime, TimeoutSnapshot,
},
uwhscores::GameInfo,
};

Expand Down Expand Up @@ -186,9 +188,9 @@ pub(in super::super) fn build_timeout_ribbon<'a>(
let black = match snapshot.timeout {
TimeoutSnapshot::None => make_multi_label_message_button(
("BLACK", "TIMEOUT"),
tm.can_start_b_timeout()
tm.can_start_team_timeout(GameColor::Black)
.ok()
.map(|_| Message::BlackTimeout(false)),
.map(|_| Message::TeamTimeout(GameColor::Black, false)),
)
.style(ButtonStyle::Black),
TimeoutSnapshot::Black(_) => {
Expand All @@ -198,9 +200,9 @@ pub(in super::super) fn build_timeout_ribbon<'a>(
TimeoutSnapshot::White(_) | TimeoutSnapshot::Ref(_) | TimeoutSnapshot::PenaltyShot(_) => {
make_multi_label_message_button(
("SWITCH TO", "BLACK"),
tm.can_switch_to_b_timeout()
tm.can_switch_to_team_timeout(GameColor::Black)
.ok()
.map(|_| Message::BlackTimeout(true)),
.map(|_| Message::TeamTimeout(GameColor::Black, true)),
)
.style(ButtonStyle::Black)
}
Expand All @@ -209,9 +211,9 @@ pub(in super::super) fn build_timeout_ribbon<'a>(
let white = match snapshot.timeout {
TimeoutSnapshot::None => make_multi_label_message_button(
("WHITE", "TIMEOUT"),
tm.can_start_w_timeout()
tm.can_start_team_timeout(GameColor::White)
.ok()
.map(|_| Message::WhiteTimeout(false)),
.map(|_| Message::TeamTimeout(GameColor::White, false)),
)
.style(ButtonStyle::White),
TimeoutSnapshot::White(_) => {
Expand All @@ -221,9 +223,9 @@ pub(in super::super) fn build_timeout_ribbon<'a>(
TimeoutSnapshot::Black(_) | TimeoutSnapshot::Ref(_) | TimeoutSnapshot::PenaltyShot(_) => {
make_multi_label_message_button(
("SWITCH TO", "WHITE"),
tm.can_switch_to_w_timeout()
tm.can_start_team_timeout(GameColor::White)
.ok()
.map(|_| Message::WhiteTimeout(true)),
.map(|_| Message::TeamTimeout(GameColor::White, true)),
)
.style(ButtonStyle::White)
}
Expand Down
26 changes: 26 additions & 0 deletions refbox/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ impl Default for UwhScores {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UwhPortal {
pub url: String,
pub email: String,
pub password: String,
}

impl Default for UwhPortal {
fn default() -> Self {
Self {
url: "https://api.uwhscores.prod.zmvp.host".to_string(),
email: String::new(),
password: String::new(),
}
}
}

#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Config {
pub mode: Mode,
Expand All @@ -50,6 +67,7 @@ pub struct Config {
pub game: Game,
pub hardware: Hardware,
pub uwhscores: UwhScores,
pub uwhportal: UwhPortal,
pub sound: SoundSettings,
}

Expand Down Expand Up @@ -84,6 +102,14 @@ mod test {
assert_eq!(deser, Ok(u));
}

#[test]
fn test_ser_uwhportal() {
let u: UwhPortal = Default::default();
let serialized = toml::to_string(&u).unwrap();
let deser = toml::from_str(&serialized);
assert_eq!(deser, Ok(u));
}

#[test]
fn test_ser_config() {
let config: Config = Default::default();
Expand Down
1 change: 1 addition & 0 deletions refbox/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {

let log_config = log_config
.logger(Logger::builder().build(APP_NAME, log_level)) // Setup the logging from the refbox app to use `log_level`
.logger(Logger::builder().build("uwh_common", log_level)) // Setup the logging from mio to use `LevelFilter::Warn`
.build(root)
.unwrap();

Expand Down
Loading

0 comments on commit 79d99f8

Please sign in to comment.