Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tracing::{error, info, trace};
use crate::{
components::{
manual_start_number_input::ManualStartNumberInput, racers::Racers, races_list::RacesList,
track_start::TrackStart, upload_results::UploadResults,
track, upload_results::UploadResults,
},
config::Config,
printer::print_result,
Expand Down Expand Up @@ -186,8 +186,8 @@ pub fn App() -> Element {
Some(Ok(race)) => rsx! {
if show_starts() {
div { class: "d-flex flex-column row-gap-1 mb-1",
for (track , start) in race.tracks_with_start() {
TrackStart { track, start }
for track in race.tracks_stats() {
track::Track { track }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ pub mod racers;
pub mod races_list;
pub mod th;
pub mod time_input;
pub mod track_start;
pub mod track;
pub mod upload_results;
34 changes: 20 additions & 14 deletions src/components/track_start.rs → src/components/track.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
use std::time::Duration;

use crate::time_utils::format_time_delta_secs;
use chrono::{DateTime, Utc};
use crate::{race::TrackStats, time_utils::format_time_delta_secs};
use chrono::Utc;
use dioxus::prelude::*;

use crate::{
components::{app::Action, time_input::TimeInput},
race::Track,
};
use crate::components::{app::Action, time_input::TimeInput};

#[component]
pub fn TrackStart(track: Track, start: Option<DateTime<Utc>>) -> Element {
pub fn Track(track: TrackStats) -> Element {
let mut editing = use_signal(|| false);
let track2 = track.clone();
let track2 = track.track.clone();
let time_since_start = use_signal(|| None);

use_hook(move || {
Expand All @@ -21,32 +18,41 @@ pub fn TrackStart(track: Track, start: Option<DateTime<Utc>>) -> Element {
async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
time_since_start
.set(start.map(|start| Some(Utc::now().signed_duration_since(start))));
time_since_start.set(
track
.start
.map(|start| Some(Utc::now().signed_duration_since(start))),
);
}
}
});
});

rsx! {
div { class: "input-group", style: "width: 490px",
div { class: "input-group", style: "width: 580px",
span {
class: "input-group-text justify-content-end",
style: "width: 150px",
"{track}"
"{track.track}"
}
span {
class: "input-group-text justify-content-end",
style: "width: 90px",
"{track.finished}/{track.racers}"
}
span { class: "input-group-text", style: "width: 90px",
if let Some(time_since_start) = time_since_start() {
"{format_time_delta_secs(time_since_start)}"
}
}
TimeInput {
time: start,
time: track.start,
editing,
span_class: "input-group-text flex-grow-1",
onsave: move |time_option| {
if let Some(time) = time_option {
use_coroutine_handle::<Action>().send(Action::Start(track.clone(), time));
use_coroutine_handle::<Action>()
.send(Action::Start(track.track.clone(), time));
}
},
}
Expand Down
166 changes: 154 additions & 12 deletions src/race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ impl fmt::Display for Track {
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct TrackStats {
pub track: Track,
pub start: Option<DateTime<Utc>>,
pub racers: usize,
pub finished: usize,
}

#[derive(Clone, PartialEq)]
pub struct Race {
pub id: u32,
Expand Down Expand Up @@ -89,6 +97,18 @@ pub enum RacerField {
Time,
}

fn track_sort_key(track: &Track) -> (u32, String) {
match track
.0
.split_whitespace()
.next()
.and_then(|s| s.parse::<u32>().ok())
{
Some(distance) => (distance, track.0.clone()),
None => (0, track.0.clone()),
}
}

impl Racer {
pub fn cmp_by(&self, other: &Self, field: RacerField) -> Ordering {
match field {
Expand Down Expand Up @@ -147,14 +167,7 @@ fn extract_tracks(api_result: &[crate::restclient::Racer]) -> Vec<Track> {
.into_iter()
.collect();

tracks.sort_by_key(|track| {
track
.0
.split_whitespace()
.next()
.and_then(|s| s.parse::<u32>().ok())
.unwrap_or(0)
});
tracks.sort_by_key(track_sort_key);
tracks
}

Expand Down Expand Up @@ -232,11 +245,37 @@ impl Race {
Ok(race)
}

pub fn tracks_with_start(&self) -> Vec<(Track, Option<DateTime<Utc>>)> {
self.tracks
pub fn tracks_stats(&self) -> Vec<TrackStats> {
let mut tracks: HashMap<&Track, TrackStats> = self
.tracks
.iter()
.map(|track| (track.clone(), self.track_starts.get(track).copied()))
.collect()
.map(|track| {
(
track,
TrackStats {
track: track.clone(),
start: self.track_starts.get(track).copied(),
racers: 0,
finished: 0,
},
)
})
.collect();

for racer in self.racers.iter() {
if let Some(track) = tracks.get_mut(&racer.track) {
track.racers += 1;
if racer.finish.is_some() {
track.finished += 1;
}
} else {
error!("Could not find track: {}", racer.track);
}
}

let mut tracks: Vec<TrackStats> = tracks.into_values().collect();
tracks.sort_by_key(|track_stats| track_sort_key(&track_stats.track));
tracks
}

pub fn start(&mut self, track: Track, time: DateTime<Utc>) {
Expand Down Expand Up @@ -378,6 +417,109 @@ impl Race {
mod tests {
use crate::race::*;

#[test]
fn track_sort() {
assert_eq!(
(4, "4 Km".to_string()),
track_sort_key(&Track("4 Km".to_string()))
);
assert_eq!(
(10, "10 Km".to_string()),
track_sort_key(&Track("10 Km".to_string()))
);
assert_eq!(
(0, "Detska trat".to_string()),
track_sort_key(&Track("Detska trat".to_string()))
);
}

#[test]
fn track_stats() {
let track_10km = Track("10 Km".to_string());
let track_4km = Track("4 Km".to_string());
let track_childs = Track("Detska trat".to_string());
let start = Utc::now();

let racers = vec![
Racer {
id: 22,
start_number: StartNumber(241),
tag: "tag2".into(),
first_name: "Bob".into(),
last_name: "Jones".into(),
track: track_4km.clone(),
categories: vec![],
start: Some(start),
finish: None,
time: None,
track_rank: None,
categories_rank: HashMap::new(),
},
Racer {
id: 1,
start_number: StartNumber(200),
tag: "tag2".into(),
first_name: "Bob".into(),
last_name: "Jones".into(),
track: track_10km.clone(),
categories: vec![],
start: Some(start),
finish: Some(start + chrono::Duration::seconds(15)),
time: None,
track_rank: None,
categories_rank: HashMap::new(),
},
Racer {
id: 2,
start_number: StartNumber(201),
tag: "tag2".into(),
first_name: "Bob".into(),
last_name: "Jones".into(),
track: track_4km.clone(),
categories: vec![],
start: Some(start),
finish: Some(start + chrono::Duration::seconds(15)),
time: None,
track_rank: None,
categories_rank: HashMap::new(),
},
];

let race = Race {
id: 1,
racers,
categories: vec![],
tracks: vec![track_10km.clone(), track_4km.clone(), track_childs.clone()],
track_starts: HashMap::new(),
log: Rc::new(RefCell::new(RaceEvents::load(100000))),
};

let stats = race.tracks_stats();
assert_eq!(
stats,
vec![
TrackStats {
track: track_childs,
start: None,
racers: 0,
finished: 0
},
TrackStats {
track: track_4km,
start: None,
racers: 2,
finished: 1
},
TrackStats {
track: track_10km,
start: None,
racers: 1,
finished: 1
}
]
);
}

#[test]
fn test_calculate_track_rank() {
let track = Track("Track 1".to_string());
Expand Down
Loading