Refactor duplicate code, lints, correct engine status render
This commit is contained in:
@@ -60,17 +60,10 @@ pub struct UserConfig {
|
|||||||
impl UserConfig {
|
impl UserConfig {
|
||||||
pub fn new(month: u8, year: i32) -> Self {
|
pub fn new(month: u8, year: i32) -> Self {
|
||||||
let month = Month::try_from(month).unwrap();
|
let month = Month::try_from(month).unwrap();
|
||||||
|
|
||||||
let total_days = month.num_days(year).unwrap();
|
let total_days = month.num_days(year).unwrap();
|
||||||
|
let total_slots = compute_total_slots(total_days);
|
||||||
let total_slots = (1..=total_days)
|
let total_holiday_slots =
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
compute_total_holiday_slots(total_days, month.number_from_month(), year, &[]);
|
||||||
.sum();
|
|
||||||
|
|
||||||
let total_holiday_slots = (1..=total_days)
|
|
||||||
.filter(|&d| Day(d).is_weekend(month.number_from_month(), year))
|
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
month,
|
month,
|
||||||
@@ -86,7 +79,12 @@ impl UserConfig {
|
|||||||
|
|
||||||
pub fn with_holidays(mut self, holidays: Vec<u8>) -> Self {
|
pub fn with_holidays(mut self, holidays: Vec<u8>) -> Self {
|
||||||
self.holidays = holidays;
|
self.holidays = holidays;
|
||||||
self.total_holiday_slots = self.total_holiday_slots();
|
self.total_holiday_slots = compute_total_holiday_slots(
|
||||||
|
self.total_days,
|
||||||
|
self.month.number_from_month(),
|
||||||
|
self.year,
|
||||||
|
&self.holidays,
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,21 +104,14 @@ impl UserConfig {
|
|||||||
|
|
||||||
pub fn update_month(&mut self, month: u8) {
|
pub fn update_month(&mut self, month: u8) {
|
||||||
self.month = Month::try_from(month).unwrap();
|
self.month = Month::try_from(month).unwrap();
|
||||||
|
|
||||||
self.total_days = self.month.num_days(self.year).unwrap();
|
self.total_days = self.month.num_days(self.year).unwrap();
|
||||||
|
self.total_slots = compute_total_slots(self.total_days);
|
||||||
self.total_slots = (1..=self.total_days)
|
self.total_holiday_slots = compute_total_holiday_slots(
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
self.total_days,
|
||||||
.sum();
|
self.month.number_from_month(),
|
||||||
|
self.year,
|
||||||
self.total_holiday_slots = self.total_holiday_slots()
|
&self.holidays,
|
||||||
}
|
);
|
||||||
|
|
||||||
fn total_holiday_slots(&self) -> u8 {
|
|
||||||
(1..=self.total_days)
|
|
||||||
.filter(|&d| self.is_holiday_or_weekend(Day(d)))
|
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_holiday_or_weekend(&self, day: Day) -> bool {
|
pub fn is_holiday_or_weekend(&self, day: Day) -> bool {
|
||||||
@@ -159,17 +150,10 @@ impl UserConfig {
|
|||||||
impl Default for UserConfig {
|
impl Default for UserConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let month = Month::try_from(MONTH).unwrap();
|
let month = Month::try_from(MONTH).unwrap();
|
||||||
|
|
||||||
let total_days = month.num_days(YEAR).unwrap();
|
let total_days = month.num_days(YEAR).unwrap();
|
||||||
|
let total_slots = compute_total_slots(total_days);
|
||||||
let total_slots = (1..=total_days)
|
let total_holiday_slots =
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
compute_total_holiday_slots(total_days, month.number_from_month(), YEAR, &[]);
|
||||||
.sum();
|
|
||||||
|
|
||||||
let total_holiday_slots = (1..=total_days)
|
|
||||||
.filter(|&d| Day(d).is_weekend(month.number_from_month(), YEAR))
|
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
month,
|
month,
|
||||||
@@ -189,20 +173,14 @@ impl TryFrom<UserConfigDTO> for UserConfig {
|
|||||||
|
|
||||||
fn try_from(value: UserConfigDTO) -> Result<Self, Self::Error> {
|
fn try_from(value: UserConfigDTO) -> Result<Self, Self::Error> {
|
||||||
let month = Month::try_from(value.month)?;
|
let month = Month::try_from(value.month)?;
|
||||||
|
|
||||||
let total_days = month.num_days(value.year).context("Failed to parse")?;
|
let total_days = month.num_days(value.year).context("Failed to parse")?;
|
||||||
|
let total_slots = compute_total_slots(total_days);
|
||||||
let total_slots = (1..=total_days)
|
let total_holiday_slots = compute_total_holiday_slots(
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
total_days,
|
||||||
.sum();
|
month.number_from_month(),
|
||||||
|
value.year,
|
||||||
let total_holiday_slots = (1..=total_days)
|
&value.holidays,
|
||||||
.filter(|&d| {
|
);
|
||||||
Day(d).is_weekend(month.number_from_month(), value.year)
|
|
||||||
|| value.holidays.contains(&d)
|
|
||||||
})
|
|
||||||
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
month,
|
month,
|
||||||
@@ -221,6 +199,19 @@ impl TryFrom<UserConfigDTO> for UserConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_total_slots(total_days: u8) -> u8 {
|
||||||
|
(1..=total_days)
|
||||||
|
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_total_holiday_slots(total_days: u8, month_num: u32, year: i32, holidays: &[u8]) -> u8 {
|
||||||
|
(1..=total_days)
|
||||||
|
.filter(|&d| Day(d).is_weekend(month_num, year) || holidays.contains(&d))
|
||||||
|
.map(|d| if Day(d).is_open_shift() { 2 } else { 1 })
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -181,6 +181,6 @@ fn month_to_greek(month: u32) -> &'static str {
|
|||||||
10 => "Οκτώβριος",
|
10 => "Οκτώβριος",
|
||||||
11 => "Νοέμβριος",
|
11 => "Νοέμβριος",
|
||||||
12 => "Δεκέμβριος",
|
12 => "Δεκέμβριος",
|
||||||
_ => panic!("Unable to find translation for month {}", month),
|
_ => unreachable!("Invalid month: {}", month),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ impl Scheduler {
|
|||||||
|
|
||||||
let solved_in_thread = AtomicBool::new(false);
|
let solved_in_thread = AtomicBool::new(false);
|
||||||
|
|
||||||
let sovled_state = valid_resident_ids.par_iter().find_map_any(|&id| {
|
let solved_state = valid_resident_ids.par_iter().find_map_any(|&id| {
|
||||||
let mut local_schedule = schedule.clone();
|
let mut local_schedule = schedule.clone();
|
||||||
let mut local_tracker = tracker.clone();
|
let mut local_tracker = tracker.clone();
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((solved_schedule, solved_tracker)) = sovled_state {
|
if let Some((solved_schedule, solved_tracker)) = solved_state {
|
||||||
*schedule = solved_schedule;
|
*schedule = solved_schedule;
|
||||||
*tracker = solved_tracker;
|
*tracker = solved_tracker;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ impl WorkloadBounds {
|
|||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_max_workloads(&mut self, residents: &Vec<Resident>, total_slots: u8) {
|
pub fn calculate_max_workloads(&mut self, residents: &[Resident], total_slots: u8) {
|
||||||
let non_manual_residents: Vec<_> = residents
|
let non_manual_residents: Vec<_> = residents
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|r| r.max_shifts.is_none())
|
.filter(|r| r.max_shifts.is_none())
|
||||||
@@ -64,7 +64,7 @@ impl WorkloadBounds {
|
|||||||
|
|
||||||
pub fn calculate_max_holiday_shifts(
|
pub fn calculate_max_holiday_shifts(
|
||||||
&mut self,
|
&mut self,
|
||||||
residents: &Vec<Resident>,
|
residents: &[Resident],
|
||||||
total_holiday_slots: u8,
|
total_holiday_slots: u8,
|
||||||
) {
|
) {
|
||||||
let total_residents = residents.len();
|
let total_residents = residents.len();
|
||||||
@@ -74,7 +74,7 @@ impl WorkloadBounds {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_max_by_shift_type(&mut self, residents: &Vec<Resident>) {
|
pub fn calculate_max_by_shift_type(&mut self, residents: &[Resident]) {
|
||||||
let mut upper_limits = HashMap::new();
|
let mut upper_limits = HashMap::new();
|
||||||
let shift_types = [
|
let shift_types = [
|
||||||
ShiftType::OpenFirst,
|
ShiftType::OpenFirst,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
{#if rota.metrics.length > 0}
|
{#if rota.metrics.length > 0}
|
||||||
<div class="h-px w-full bg-zinc-200"></div>
|
<div class="h-px w-full bg-zinc-200"></div>
|
||||||
<div class="flex flex-1 flex-col py-4">
|
<div class="flex flex-col py-4">
|
||||||
<p class="px-6 pb-2 text-[10px] font-black tracking-widest text-zinc-400 uppercase">
|
<p class="px-6 pb-2 text-[10px] font-black tracking-widest text-zinc-400 uppercase">
|
||||||
Δικαιωσυνη
|
Δικαιωσυνη
|
||||||
</p>
|
</p>
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="h-px w-full bg-zinc-200"></div>
|
<div class="mt-auto h-px w-full bg-zinc-200"></div>
|
||||||
<div class="space-y-4 p-4">
|
<div class="space-y-4 p-4">
|
||||||
<div
|
<div
|
||||||
class="border-l-2 py-2 pl-3 transition-colors
|
class="border-l-2 py-2 pl-3 transition-colors
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
{ value: 6, label: "Ιούνιος" },
|
{ value: 6, label: "Ιούνιος" },
|
||||||
{ value: 7, label: "Ιούλιος" },
|
{ value: 7, label: "Ιούλιος" },
|
||||||
{ value: 8, label: "Αύγουστος" },
|
{ value: 8, label: "Αύγουστος" },
|
||||||
{ value: 9, label: "Σεπτέμβιος" },
|
{ value: 9, label: "Σεπτέμβριος" },
|
||||||
{ value: 10, label: "Οκτώβριος" },
|
{ value: 10, label: "Οκτώβριος" },
|
||||||
{ value: 11, label: "Νοέμβριος" },
|
{ value: 11, label: "Νοέμβριος" },
|
||||||
{ value: 12, label: "Δεκέμβιος" }
|
{ value: 12, label: "Δεκέμβριος" }
|
||||||
];
|
];
|
||||||
|
|
||||||
const yearOptions = [2026, 2027];
|
const yearOptions = [2026, 2027];
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<select
|
<select
|
||||||
bind:value={rota.selectedMonth}
|
bind:value={rota.selectedMonth}
|
||||||
onchange={() => rota.syncProjectMonth()}
|
|
||||||
class="w-full appearance-none rounded-xl border border-zinc-200 bg-white px-4 py-3 font-semibold text-zinc-700 transition-all outline-none focus:border-zinc-400 focus:ring-4 focus:ring-zinc-100"
|
class="w-full appearance-none rounded-xl border border-zinc-200 bg-white px-4 py-3 font-semibold text-zinc-700 transition-all outline-none focus:border-zinc-400 focus:ring-4 focus:ring-zinc-100"
|
||||||
>
|
>
|
||||||
{#each monthOptions as month}
|
{#each monthOptions as month}
|
||||||
@@ -72,7 +71,6 @@
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<select
|
<select
|
||||||
bind:value={rota.selectedYear}
|
bind:value={rota.selectedYear}
|
||||||
onchange={() => rota.syncProjectMonth()}
|
|
||||||
class="w-full appearance-none rounded-xl border border-zinc-200 bg-white px-4 py-3 font-semibold text-zinc-700 transition-all outline-none focus:border-zinc-400 focus:ring-4 focus:ring-zinc-100"
|
class="w-full appearance-none rounded-xl border border-zinc-200 bg-white px-4 py-3 font-semibold text-zinc-700 transition-all outline-none focus:border-zinc-400 focus:ring-4 focus:ring-zinc-100"
|
||||||
>
|
>
|
||||||
{#each yearOptions as year}
|
{#each yearOptions as year}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
let config = rota.toDTO();
|
let config = rota.toDTO();
|
||||||
rota.engineStatus = EngineStatus.Running;
|
rota.engineStatus = EngineStatus.Running;
|
||||||
rota.lastMessage = "";
|
rota.lastMessage = "";
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rota.solution = await invoke<MonthlyScheduleDTO>("generate", { config });
|
rota.solution = await invoke<MonthlyScheduleDTO>("generate", { config });
|
||||||
|
|||||||
@@ -39,13 +39,9 @@ export class RotaState {
|
|||||||
|
|
||||||
metrics: ResidentMetrics[] = $state([]);
|
metrics: ResidentMetrics[] = $state([]);
|
||||||
|
|
||||||
projectMonth = $state(new CalendarDate(2026, 2, 1));
|
projectMonth = $derived(new CalendarDate(this.selectedYear, this.selectedMonth, 1));
|
||||||
|
|
||||||
syncProjectMonth() {
|
|
||||||
this.projectMonth = new CalendarDate(this.selectedYear, this.selectedMonth, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
projectMonthDays = $derived(this.projectMonth.calendar.getDaysInMonth(this.projectMonth));
|
projectMonthDays = $derived(this.projectMonth.calendar.getDaysInMonth(this.projectMonth));
|
||||||
|
|
||||||
daysArray = $derived(Array.from({ length: this.projectMonthDays }, (_, i) => i + 1));
|
daysArray = $derived(Array.from({ length: this.projectMonthDays }, (_, i) => i + 1));
|
||||||
emptySlots = $derived(Array.from({ length: getDayOfWeek(this.projectMonth, "en-GB") }));
|
emptySlots = $derived(Array.from({ length: getDayOfWeek(this.projectMonth, "en-GB") }));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user