Skip to content

Commit

Permalink
Merge branch 'main' into tijl/-/add-test-impossible
Browse files Browse the repository at this point in the history
  • Loading branch information
Tijl Leenders committed Mar 23, 2024
2 parents a5c9367 + ae3ed65 commit 190ac51
Show file tree
Hide file tree
Showing 19 changed files with 571 additions and 133 deletions.
34 changes: 15 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
//! quality perception of the ZinZen® projects.
use chrono::NaiveDateTime;
use models::{activity::Activity, calendar::Calendar, goal::Goal, task::FinalTasks};
use models::{calendar::Calendar, goal::Goal, task::FinalTasks};
use serde_wasm_bindgen::{from_value, to_value};
use services::activity_generator;
use services::activity_placer;
Expand Down Expand Up @@ -94,35 +94,31 @@ pub fn run_scheduler(

calendar.add_budgets_from(goals);

//generate and place simple goal activities
let simple_goal_activities =
activity_generator::generate_simple_goal_activities(&calendar, goals);
dbg!(&simple_goal_activities);
let mut base_activities = activity_generator::get_base_activities(&calendar, goals);

//generate and place budget goal activities
let budget_goal_activities: Vec<Activity> =
activity_generator::generate_budget_goal_activities(&calendar, goals);
dbg!(&budget_goal_activities);
dbg!(&calendar);

activity_placer::place(&mut calendar, simple_goal_activities);
activity_placer::place(&mut calendar, budget_goal_activities);
base_activities = activity_placer::place(&mut calendar, base_activities);

calendar.log_impossible_min_day_budgets();

if let Some(get_to_week_min_budget_activities) =
activity_generator::generate_get_to_week_min_budget_activities(&calendar, goals)
{
activity_placer::place(&mut calendar, get_to_week_min_budget_activities);
}
let get_to_week_min_budget_activities =
activity_generator::get_budget_min_week_activities(&calendar, goals);
activity_placer::place(&mut calendar, get_to_week_min_budget_activities);
//TODO: Test that day stays below min when week min being reached so other goals can get to the week min too

calendar.log_impossible_min_week_budgets();

let top_up_week_budget_activities =
activity_generator::generate_top_up_week_budget_activities(&calendar, goals);
activity_generator::get_budget_top_up_week_activities(&calendar, goals);
activity_placer::place(&mut calendar, top_up_week_budget_activities);
//TODO: Test that day stays below min or max when week max being reachd

//TODO: Fit simple budget activities into scheduled budgets?
// No need, as simple budget activities will share the same overlay, but with less hours
// Thus, the flex will always be higher than (or equal to?) the MinDayBudget activities
// So MinDayBudget will get chosen last unless flex is equal and order happens to favor MinDayBudget
// => TODO: order activities before placing?

calendar.log_impossible_base_activities(base_activities);

calendar.print()
}
78 changes: 58 additions & 20 deletions src/models/activity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pub struct Activity {
pub min_block_size: usize,
pub max_block_size: usize,
pub calendar_overlay: Vec<Option<Weak<Hour>>>,
pub time_budgets: Vec<TimeBudget>,
pub total_duration: usize,
pub duration_left: usize,
pub status: Status,
pub deadline: NaiveDateTime,
}
impl Activity {
pub fn get_compatible_hours_overlay(
Expand Down Expand Up @@ -138,9 +138,10 @@ impl Activity {
//TODO: shouldn't this logic be in creating the activity and then set to min_block_size so we can just use that here?
let offset_size: usize = match self.activity_type {
ActivityType::SimpleGoal => self.total_duration,
ActivityType::Budget => self.min_block_size,
ActivityType::BudgetMinDay => self.min_block_size,
ActivityType::GetToMinWeekBudget => 1,
ActivityType::TopUpWeekBudget => 1,
ActivityType::SimpleFiller => self.total_duration,
};
for offset in 0..offset_size {
match &self.calendar_overlay[hour_index + offset] {
Expand Down Expand Up @@ -178,13 +179,7 @@ impl Activity {
best_scheduling_index_and_conflicts.map(|(best_index, _, size)| (best_index, size))
}

pub(crate) fn get_activities_from_simple_goal(
goal: &Goal,
calendar: &Calendar,
) -> Vec<Activity> {
if goal.children.is_some() || goal.filters.as_ref().is_some() {
return vec![];
}
pub(crate) fn get_simple_activities(goal: &Goal, calendar: &Calendar) -> Vec<Activity> {
let (adjusted_goal_start, adjusted_goal_deadline) = goal.get_adj_start_deadline(calendar);
let mut activities: Vec<Activity> = Vec::with_capacity(1);

Expand Down Expand Up @@ -213,10 +208,10 @@ impl Activity {
min_block_size,
max_block_size: min_block_size,
calendar_overlay: compatible_hours_overlay,
time_budgets: vec![],
total_duration: activity_total_duration,
duration_left: min_block_size, //TODO: Correct this - is it even necessary to have duration_left?
duration_left: activity_total_duration,
status: Status::Unprocessed,
deadline: goal.deadline,
};
dbg!(&activity);
activities.push(activity);
Expand All @@ -225,10 +220,7 @@ impl Activity {
activities
}

pub(crate) fn get_activities_from_budget_goal(
goal: &Goal,
calendar: &Calendar,
) -> Vec<Activity> {
pub(crate) fn get_budget_min_day_activities(goal: &Goal, calendar: &Calendar) -> Vec<Activity> {
if goal.filters.as_ref().is_none() {
return vec![];
}
Expand Down Expand Up @@ -273,15 +265,15 @@ impl Activity {

let activity = Activity {
goal_id: goal.id.clone(),
activity_type: ActivityType::Budget,
activity_type: ActivityType::BudgetMinDay,
title: goal.title.clone(),
min_block_size: adjusted_min_block_size,
max_block_size: config.max_per_day,
calendar_overlay: compatible_hours_overlay,
time_budgets: vec![],
total_duration: adjusted_min_block_size,
duration_left: config.min_per_day,
status: Status::Unprocessed,
deadline: goal.deadline,
};
dbg!(&activity);
activities.push(activity);
Expand Down Expand Up @@ -320,10 +312,10 @@ impl Activity {
min_block_size: 1,
max_block_size: max_hours,
calendar_overlay: compatible_hours_overlay,
time_budgets: vec![],
total_duration: max_hours,
duration_left: max_hours,
status: Status::Unprocessed,
deadline: goal_to_use.deadline,
});

activities
Expand Down Expand Up @@ -359,10 +351,10 @@ impl Activity {
min_block_size: 1,
max_block_size: max_hours,
calendar_overlay: compatible_hours_overlay,
time_budgets: vec![],
total_duration: max_hours,
duration_left: max_hours,
status: Status::Unprocessed,
deadline: goal_to_use.deadline,
});

activities
Expand Down Expand Up @@ -464,6 +456,7 @@ impl Activity {
self.status = Status::Impossible;
}
}

pub(crate) fn release_claims(&mut self) {
let mut empty_overlay: Vec<Option<Weak<Hour>>> =
Vec::with_capacity(self.calendar_overlay.capacity());
Expand All @@ -472,6 +465,50 @@ impl Activity {
}
self.calendar_overlay = empty_overlay;
}

pub(crate) fn get_simple_filler_activities(goal: &Goal, calendar: &Calendar) -> Vec<Activity> {
if goal.children.is_none() || goal.filters.as_ref().is_some() {
return vec![];
}
let (adjusted_goal_start, adjusted_goal_deadline) = goal.get_adj_start_deadline(calendar);
let mut activities: Vec<Activity> = Vec::with_capacity(1);

if let Some(activity_total_duration) = goal.min_duration {
let mut min_block_size = activity_total_duration;
if activity_total_duration > 8 {
min_block_size = 1;
//todo!() //split into multiple activities so flexibilities are correct??
// or yield flex 1 or maximum of the set from activity.flex()?
};

let filters_option: Option<Filters> = calendar.get_filters_for(goal.id.clone());

let compatible_hours_overlay = Activity::get_compatible_hours_overlay(
calendar,
filters_option,
adjusted_goal_start,
adjusted_goal_deadline,
goal.not_on.clone(),
);

let activity = Activity {
goal_id: goal.id.clone(),
activity_type: ActivityType::SimpleFiller,
title: goal.title.clone(),
min_block_size,
max_block_size: min_block_size,
calendar_overlay: compatible_hours_overlay,
total_duration: activity_total_duration,
duration_left: activity_total_duration,
status: Status::Unprocessed,
deadline: goal.deadline,
};
dbg!(&activity);
activities.push(activity);
}

activities
}
}

#[derive(Debug, PartialEq, Clone, Deserialize)]
Expand All @@ -485,9 +522,10 @@ pub enum Status {
#[derive(Clone, Debug, PartialEq)]
pub enum ActivityType {
SimpleGoal,
Budget,
BudgetMinDay,
GetToMinWeekBudget,
TopUpWeekBudget,
SimpleFiller,
}

impl fmt::Debug for Activity {
Expand Down
3 changes: 2 additions & 1 deletion src/models/budget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Budget {
ActivityType::SimpleGoal => {
budget_cut_off_number = time_budget.min_scheduled;
}
ActivityType::Budget => {
ActivityType::BudgetMinDay => {
budget_cut_off_number = time_budget.min_scheduled;
}
ActivityType::GetToMinWeekBudget => {
Expand All @@ -61,6 +61,7 @@ impl Budget {
ActivityType::TopUpWeekBudget => {
budget_cut_off_number = time_budget.max_scheduled;
}
ActivityType::SimpleFiller => budget_cut_off_number = time_budget.min_scheduled,
}
//figure out how many of the hours in hour_index till hour_index + offset are in the time_budget window
let mut hours_in_time_budget_window = 0;
Expand Down
25 changes: 25 additions & 0 deletions src/models/calendar.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::activity::Activity;
use super::budget::{get_time_budgets_from, Budget, TimeBudgetType};
use super::goal::Goal;
use super::task::{DayTasks, FinalTasks, Task};
Expand Down Expand Up @@ -70,6 +71,15 @@ impl Calendar {
date_time_of_index_to_test.weekday()
}

pub fn is_budget(&self, goal_id: String) -> bool {
for budget in self.budgets.iter() {
if budget.participating_goals.contains(&goal_id) {
return true;
}
}
false
}

pub fn get_index_of(&self, date_time: NaiveDateTime) -> usize {
if date_time < self.start_date_time.sub(Duration::days(1))
|| date_time > self.end_date_time.add(Duration::days(1))
Expand Down Expand Up @@ -294,6 +304,21 @@ impl Calendar {
self.impossible_activities.extend(impossible_activities);
}

pub fn log_impossible_base_activities(&mut self, activities: Vec<Activity>) {
for activity in activities {
if activity.status == super::activity::Status::Impossible
&& activity.deadline.year() != 1970
{
self.impossible_activities.push(ImpossibleActivity {
id: activity.goal_id.clone(),
hours_missing: activity.duration_left,
period_start_date_time: self.start_date_time,
period_end_date_time: self.end_date_time,
})
}
}
}

fn impossible_activities(&mut self) -> Vec<ImpossibleActivity> {
let mut impossible_activities = vec![];
for budget in &self.budgets {
Expand Down
14 changes: 14 additions & 0 deletions src/models/goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ impl Goal {
if self.deadline.year() == 1970 {
adjusted_goal_deadline = calendar.end_date_time;
}

if self.filters.is_none() {
return (adjusted_goal_start, adjusted_goal_deadline);
}
Expand All @@ -78,4 +79,17 @@ impl Goal {
}
(adjusted_goal_start, adjusted_goal_deadline)
}

/// Get parent goal of this goal based in provided list of goals
pub fn get_parent_goal(&self, goals: &[Goal]) -> Option<Goal> {
let parent_goal = goals.iter().find(|goal| {
if let Some(childs) = &goal.children {
childs.contains(&self.id)
} else {
false
}
});

parent_goal.cloned()
}
}
Loading

0 comments on commit 190ac51

Please sign in to comment.