Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tijl/-/add-simple-without-deadline #453

Merged
merged 30 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8e98edd
add failing test case
Mar 19, 2024
919b256
Merge branch 'main' into tijl/-/add-simple-without-deadline
Mar 22, 2024
ed18dc6
fix expectation makes test pass
Mar 22, 2024
6a350bd
improve test to hit both branches
Mar 22, 2024
546fb70
update expectation for improved test
Mar 22, 2024
533f658
check if impossible based on activity.deadline
Mar 22, 2024
07ed918
place simple activities without deadline last?
Mar 22, 2024
600fed7
check that order shouldn't matter
Mar 22, 2024
a24b18f
add ActivityType SimpleFiller
Mar 23, 2024
9eb4439
extract method to clean up run_scheduler
Mar 23, 2024
33ea74f
clean up verbosity from unnecessary option
Mar 23, 2024
ad542b0
clippy needless borrow
Mar 23, 2024
e643e82
rename ActivityType to BudgetMinDay
Mar 23, 2024
a273dc1
stub parallel function
Mar 23, 2024
ca94089
assume start/deadline consistent in tree
Mar 23, 2024
bdd3c16
fix input
Mar 23, 2024
096a851
gen_activities and generate_activities identical result
Mar 23, 2024
8c2b926
remove unnecessary option handling
Mar 23, 2024
88b4744
no need to look for goal
Mar 23, 2024
0483b48
imrpove naming
Mar 23, 2024
dee6eec
better names
Mar 23, 2024
ab595bf
better names
Mar 23, 2024
877e3e5
remove refactored function
Mar 23, 2024
2c6ca05
include BudgetMinDay in base_activities
Mar 23, 2024
3242c66
clear up if/else mess
Mar 23, 2024
0a0b6d6
remove unused time budgets
Mar 23, 2024
f133b25
add todo
Mar 23, 2024
0142184
remove unnecessary check / fixes test
Mar 23, 2024
413994b
done todo
Mar 23, 2024
52d34f3
add todo
Mar 23, 2024
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
43 changes: 15 additions & 28 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,44 +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);

let simple_filler_activities =
activity_generator::generate_simple_filler_goal_activities(&calendar, goals);
dbg!(&simple_filler_activities);

//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);

let activities: Vec<Activity> = [
simple_goal_activities,
simple_filler_activities,
budget_goal_activities,
]
.concat();
activity_placer::place(&mut calendar, 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()
}
53 changes: 19 additions & 34 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,17 +179,8 @@ 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,
parent_goal: Option<Goal>,
) -> Vec<Activity> {
if goal.children.is_some() || goal.filters.as_ref().is_some() {
return vec![];
}

let (adjusted_goal_start, adjusted_goal_deadline) =
goal.get_adj_start_deadline(calendar, parent_goal);
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);

if let Some(activity_total_duration) = goal.min_duration {
Expand Down Expand Up @@ -216,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: activity_total_duration,
status: Status::Unprocessed,
deadline: goal.deadline,
};
dbg!(&activity);
activities.push(activity);
Expand All @@ -228,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 All @@ -240,8 +229,7 @@ impl Activity {
return vec![];
}
}
let (adjusted_goal_start, adjusted_goal_deadline) =
goal.get_adj_start_deadline(calendar, None);
let (adjusted_goal_start, adjusted_goal_deadline) = goal.get_adj_start_deadline(calendar);
let mut activities: Vec<Activity> = Vec::with_capacity(1);

for day in 0..(adjusted_goal_deadline - adjusted_goal_start).num_days() as u64 {
Expand Down Expand Up @@ -277,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 @@ -324,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 @@ -363,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 @@ -468,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 @@ -477,16 +466,11 @@ impl Activity {
self.calendar_overlay = empty_overlay;
}

pub(crate) fn get_filler_activities_from_simple_goal(
goal: &Goal,
calendar: &Calendar,
parent_goal: Option<Goal>,
) -> Vec<Activity> {
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, parent_goal);
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 {
Expand All @@ -509,15 +493,15 @@ impl Activity {

let activity = Activity {
goal_id: goal.id.clone(),
activity_type: ActivityType::SimpleGoal,
activity_type: ActivityType::SimpleFiller,
title: goal.title.clone(),
min_block_size,
max_block_size: min_block_size,
calendar_overlay: compatible_hours_overlay,
time_budgets: vec![],
total_duration: activity_total_duration,
duration_left: activity_total_duration,
status: Status::Unprocessed,
deadline: goal.deadline,
};
dbg!(&activity);
activities.push(activity);
Expand All @@ -538,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
18 changes: 1 addition & 17 deletions src/models/goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ pub struct BudgetConfig {
}

impl Goal {
pub fn get_adj_start_deadline(
&self,
calendar: &Calendar,
parent_goal: Option<Goal>,
) -> (NaiveDateTime, NaiveDateTime) {
pub fn get_adj_start_deadline(&self, calendar: &Calendar) -> (NaiveDateTime, NaiveDateTime) {
let mut adjusted_goal_start = self.start;
if self.start.year() == 1970 || self.start < calendar.start_date_time {
adjusted_goal_start = calendar.start_date_time;
Expand All @@ -60,18 +56,6 @@ impl Goal {
adjusted_goal_deadline = calendar.end_date_time;
}

// Make sure child goal not fall outside of parent goal start and deadline
if let Some(parent_goal) = parent_goal {
// means this is a child goal
if adjusted_goal_start < parent_goal.start {
adjusted_goal_start = parent_goal.start;
}
if adjusted_goal_deadline > parent_goal.deadline && parent_goal.deadline.year() != 1970
{
adjusted_goal_deadline = parent_goal.deadline;
}
}

if self.filters.is_none() {
return (adjusted_goal_start, adjusted_goal_deadline);
}
Expand Down
Loading
Loading