Separate user configs from resident workload bounds
This commit is contained in:
@@ -2,6 +2,7 @@ use serde::{ser::SerializeMap, Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
bounds::WorkloadBounds,
|
||||
config::UserConfig,
|
||||
resident::Resident,
|
||||
slot::{weekday_to_greek, Day, ShiftPosition, Slot},
|
||||
@@ -73,7 +74,7 @@ impl MonthlySchedule {
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn is_per_shift_threshold_met(&self, config: &UserConfig) -> bool {
|
||||
pub fn is_per_shift_threshold_met(&self, config: &UserConfig, bounds: &WorkloadBounds) -> bool {
|
||||
for res in &config.residents {
|
||||
for stype in [
|
||||
ShiftType::OpenFirst,
|
||||
@@ -81,8 +82,7 @@ impl MonthlySchedule {
|
||||
ShiftType::Closed,
|
||||
] {
|
||||
let count = self.count_shifts(&res.id, Some(stype.clone()));
|
||||
if let Some(&threshold) = config.shift_type_threshold.get(&(res.id.clone(), stype))
|
||||
{
|
||||
if let Some(&threshold) = bounds.min_by_shift_type.get(&(res.id.clone(), stype)) {
|
||||
if count < threshold as usize {
|
||||
return false;
|
||||
}
|
||||
@@ -111,12 +111,17 @@ impl MonthlySchedule {
|
||||
///
|
||||
/// @slot points to an occupied slot
|
||||
/// @config info manually set on the GUI by the user
|
||||
pub fn restrictions_violated(&self, slot: &Slot, config: &UserConfig) -> bool {
|
||||
pub fn restrictions_violated(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
config: &UserConfig,
|
||||
bounds: &WorkloadBounds,
|
||||
) -> bool {
|
||||
self.same_resident_in_consecutive_days(slot)
|
||||
|| self.has_toxic_pair(slot, config)
|
||||
|| self.is_workload_unbalanced(slot, config)
|
||||
|| self.is_holiday_workload_imbalanced(slot, config)
|
||||
|| self.is_shift_type_distribution_unfair(slot, config)
|
||||
|| self.is_workload_unbalanced(slot, config, bounds)
|
||||
|| self.is_holiday_workload_imbalanced(slot, config, bounds)
|
||||
|| self.is_shift_type_distribution_unfair(slot, bounds)
|
||||
}
|
||||
|
||||
/// same_resident_in_consecutive_days
|
||||
@@ -160,7 +165,12 @@ impl MonthlySchedule {
|
||||
}
|
||||
|
||||
/// is_workload_unbalanced
|
||||
pub fn is_workload_unbalanced(&self, slot: &Slot, config: &UserConfig) -> bool {
|
||||
pub fn is_workload_unbalanced(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
config: &UserConfig,
|
||||
bounds: &WorkloadBounds,
|
||||
) -> bool {
|
||||
let res_id = match self.get_resident_id(slot) {
|
||||
Some(id) => id,
|
||||
None => return false,
|
||||
@@ -169,7 +179,7 @@ impl MonthlySchedule {
|
||||
if let Some(resident) = config.residents.iter().find(|r| &r.id == res_id) {
|
||||
let current_workload = self.current_workload(resident);
|
||||
|
||||
if let Some(&limit) = config.workload_limits.get(res_id) {
|
||||
if let Some(&limit) = bounds.max_workloads.get(res_id) {
|
||||
let mut workload_limit = limit;
|
||||
if resident.reduced_load {
|
||||
workload_limit -= 1;
|
||||
@@ -185,7 +195,12 @@ impl MonthlySchedule {
|
||||
}
|
||||
|
||||
/// is_holiday_workload_imbalanced
|
||||
pub fn is_holiday_workload_imbalanced(&self, slot: &Slot, config: &UserConfig) -> bool {
|
||||
pub fn is_holiday_workload_imbalanced(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
config: &UserConfig,
|
||||
bounds: &WorkloadBounds,
|
||||
) -> bool {
|
||||
if !config.is_holiday_or_weekend_slot(slot.day.0) {
|
||||
return false;
|
||||
}
|
||||
@@ -198,7 +213,7 @@ impl MonthlySchedule {
|
||||
if let Some(resident) = config.residents.iter().find(|r| &r.id == res_id) {
|
||||
let current_holiday_workload = self.current_holiday_workload(resident, config);
|
||||
|
||||
if let Some(&holiday_limit) = config.holiday_limits.get(res_id) {
|
||||
if let Some(&holiday_limit) = bounds.max_holiday_shifts.get(res_id) {
|
||||
if current_holiday_workload > holiday_limit as usize {
|
||||
return true;
|
||||
}
|
||||
@@ -209,7 +224,7 @@ impl MonthlySchedule {
|
||||
}
|
||||
|
||||
/// is_shift_type_distribution_unfair
|
||||
pub fn is_shift_type_distribution_unfair(&self, slot: &Slot, config: &UserConfig) -> bool {
|
||||
pub fn is_shift_type_distribution_unfair(&self, slot: &Slot, bounds: &WorkloadBounds) -> bool {
|
||||
let resident_id = match self.get_resident_id(slot) {
|
||||
Some(id) => id,
|
||||
None => return false,
|
||||
@@ -226,8 +241,8 @@ impl MonthlySchedule {
|
||||
|
||||
let current_count = self.count_shifts(resident_id, Some(current_shift_type.clone()));
|
||||
|
||||
if let Some(&limit) = config
|
||||
.shift_type_limits
|
||||
if let Some(&limit) = bounds
|
||||
.max_by_shift_type
|
||||
.get(&(resident_id.clone(), current_shift_type.clone()))
|
||||
{
|
||||
return current_count > limit as usize;
|
||||
@@ -325,6 +340,7 @@ mod tests {
|
||||
use rstest::{fixture, rstest};
|
||||
|
||||
use crate::{
|
||||
bounds::WorkloadBounds,
|
||||
config::UserConfig,
|
||||
resident::Resident,
|
||||
schedule::{Day, MonthlySchedule, Slot},
|
||||
@@ -413,7 +429,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_is_workload_unbalanced(mut schedule: MonthlySchedule, mut config: UserConfig) {
|
||||
fn test_is_workload_unbalanced(mut schedule: MonthlySchedule, config: UserConfig) {
|
||||
let slot_1 = Slot::new(Day(1), ShiftPosition::First);
|
||||
let slot_2 = Slot::new(Day(1), ShiftPosition::Second);
|
||||
let slot_3 = Slot::new(Day(2), ShiftPosition::First);
|
||||
@@ -421,21 +437,23 @@ mod tests {
|
||||
let stefanos = &config.residents[0];
|
||||
let iordanis = &config.residents[1];
|
||||
|
||||
config.workload_limits.insert("1".to_string(), 1);
|
||||
config.workload_limits.insert("2".to_string(), 2);
|
||||
let mut bounds = WorkloadBounds::new();
|
||||
|
||||
bounds.max_workloads.insert("1".to_string(), 1);
|
||||
bounds.max_workloads.insert("2".to_string(), 2);
|
||||
|
||||
schedule.insert(slot_1, &stefanos);
|
||||
assert!(!schedule.is_workload_unbalanced(&slot_1, &config));
|
||||
assert!(!schedule.is_workload_unbalanced(&slot_1, &config, &bounds));
|
||||
|
||||
schedule.insert(slot_2, &iordanis);
|
||||
assert!(!schedule.is_workload_unbalanced(&slot_2, &config));
|
||||
assert!(!schedule.is_workload_unbalanced(&slot_2, &config, &bounds));
|
||||
|
||||
schedule.insert(slot_3, &stefanos);
|
||||
assert!(schedule.is_workload_unbalanced(&slot_3, &config));
|
||||
assert!(schedule.is_workload_unbalanced(&slot_3, &config, &bounds));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_is_holiday_workload_imbalanced(mut schedule: MonthlySchedule, mut config: UserConfig) {
|
||||
fn test_is_holiday_workload_imbalanced(mut schedule: MonthlySchedule, config: UserConfig) {
|
||||
let slot_1 = Slot::new(Day(1), ShiftPosition::First);
|
||||
let slot_2 = Slot::new(Day(1), ShiftPosition::Second);
|
||||
let slot_7 = Slot::new(Day(7), ShiftPosition::First);
|
||||
@@ -443,16 +461,18 @@ mod tests {
|
||||
let stefanos = &config.residents[0];
|
||||
let iordanis = &config.residents[1];
|
||||
|
||||
config.holiday_limits.insert("1".to_string(), 1);
|
||||
config.holiday_limits.insert("2".to_string(), 1);
|
||||
let mut bounds = WorkloadBounds::new();
|
||||
|
||||
bounds.max_holiday_shifts.insert("1".to_string(), 1);
|
||||
bounds.max_holiday_shifts.insert("2".to_string(), 1);
|
||||
|
||||
schedule.insert(slot_1, &stefanos);
|
||||
assert!(!schedule.is_holiday_workload_imbalanced(&slot_1, &config));
|
||||
assert!(!schedule.is_holiday_workload_imbalanced(&slot_1, &config, &bounds));
|
||||
|
||||
schedule.insert(slot_2, &iordanis);
|
||||
assert!(!schedule.is_holiday_workload_imbalanced(&slot_2, &config));
|
||||
assert!(!schedule.is_holiday_workload_imbalanced(&slot_2, &config, &bounds));
|
||||
|
||||
schedule.insert(slot_7, &stefanos);
|
||||
assert!(schedule.is_holiday_workload_imbalanced(&slot_7, &config));
|
||||
assert!(schedule.is_holiday_workload_imbalanced(&slot_7, &config, &bounds));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user