Tark_Wight

DAG + CMP

Dec 22nd, 2025
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 13.59 KB | Source Code | 0 0
  1. use async_trait::async_trait;
  2. use chrono::{DateTime, Utc, Duration, NaiveDate};
  3. use std::collections::{HashMap, HashSet, VecDeque};
  4. use std::sync::Arc;
  5.  
  6. use crate::domain::{Task, Schedule, Dependency, DepType};
  7. use crate::infra::errors::{AppError, AppResult};
  8. use crate::infra::repositories::ScheduleRepository;
  9. use crate::utils::Id;
  10.  
  11. struct TmpSchedule {
  12.     es: DateTime<Utc>,
  13.     ef: DateTime<Utc>,
  14.     ls: DateTime<Utc>,
  15.     lf: DateTime<Utc>,
  16.     slack: i64,
  17.     is_critical: bool,
  18. }
  19.  
  20. #[async_trait]
  21. pub trait ScheduleService: Send + Sync {
  22.     async fn reverse_schedule(&self, project_id: Id) -> AppResult<Vec<Task>>;
  23.     async fn compute_schedule(&self, tasks: &[Task]) -> AppResult<Vec<Schedule>>;
  24. }
  25.  
  26. pub struct ScheduleServiceImpl {
  27.     schedule_repo: Arc<dyn ScheduleRepository>,
  28. }
  29.  
  30. impl ScheduleServiceImpl {
  31.     pub fn new(schedule_repo: Arc<dyn ScheduleRepository>) -> Self {
  32.         Self { schedule_repo }
  33.     }
  34.  
  35.     fn topological_sort(&self, tasks: &[Task], successors: &HashMap<Id, Vec<&Dependency>>) -> Vec<Id> {
  36.         let mut in_degree: HashMap<Id, usize> = HashMap::new();
  37.        
  38.         for task in tasks {
  39.             in_degree.insert(task.id, 0);
  40.         }
  41.        
  42.         for task in tasks {
  43.             if let Some(deps) = successors.get(&task.id) {
  44.                 for dep in deps {
  45.                     *in_degree.entry(dep.to_task_id).or_insert(0) += 1;
  46.                 }
  47.             }
  48.         }
  49.        
  50.         let mut queue: VecDeque<Id> = VecDeque::new();
  51.         for task in tasks {
  52.             if in_degree.get(&task.id) == Some(&0) {
  53.                 queue.push_back(task.id);
  54.             }
  55.         }
  56.        
  57.         let mut result: Vec<Id> = Vec::new();
  58.         while let Some(task_id) = queue.pop_front() {
  59.             result.push(task_id);
  60.            
  61.             if let Some(deps) = successors.get(&task_id) {
  62.                 for dep in deps {
  63.                     if let Some(deg) = in_degree.get_mut(&dep.to_task_id) {
  64.                         *deg -= 1;
  65.                         if *deg == 0 {
  66.                             queue.push_back(dep.to_task_id);
  67.                         }
  68.                     }
  69.                 }
  70.             }
  71.         }
  72.        
  73.         result
  74.     }
  75.  
  76.     fn compute_full_schedule(
  77.         &self,
  78.         tasks: &mut [Task],
  79.         dependencies: &[Dependency],
  80.         project_start_date: Option<NaiveDate>,
  81.         project_due_date: NaiveDate,
  82.     ) -> AppResult<()> {
  83.         if tasks.is_empty() {
  84.             return Ok(());
  85.         }
  86.  
  87.         let mut successors: HashMap<Id, Vec<&Dependency>> = HashMap::new();
  88.         let mut predecessors: HashMap<Id, Vec<&Dependency>> = HashMap::new();
  89.  
  90.         for dep in dependencies {
  91.             successors.entry(dep.from_task_id).or_default().push(dep);
  92.             predecessors.entry(dep.to_task_id).or_default().push(dep);
  93.         }
  94.  
  95.         let mut task_by_id: HashMap<Id, usize> = HashMap::new();
  96.         for (idx, task) in tasks.iter().enumerate() {
  97.             task_by_id.insert(task.id, idx);
  98.         }
  99.  
  100.         let project_start = project_start_date
  101.             .unwrap_or(project_due_date)
  102.             .and_hms_opt(0, 0, 0)
  103.             .unwrap()
  104.             .and_utc();
  105.  
  106.         let deadline = project_due_date
  107.             .and_hms_opt(23, 59, 59)
  108.             .unwrap()
  109.             .and_utc();
  110.  
  111.         let mut tmp: HashMap<Id, TmpSchedule> = HashMap::new();
  112.         let topo_order = self.topological_sort(tasks, &successors);
  113.  
  114.         for task_id in &topo_order {
  115.             if let Some(&task_idx) = task_by_id.get(task_id) {
  116.                 let task = &tasks[task_idx];
  117.                 let dur = Duration::seconds(task.estimated_duration.unwrap_or(0));
  118.  
  119.                 let es = if let Some(pred_deps) = predecessors.get(task_id) {
  120.                     let mut max_es = project_start;
  121.                    
  122.                     for dep in pred_deps {
  123.                         if let Some(pred_sched) = tmp.get(&dep.from_task_id) {
  124.                             let gap = Duration::seconds(dep.min_gap);
  125.                            
  126.                             let candidate_es = match dep.dep_type {
  127.                                 DepType::FS => pred_sched.ef + gap,
  128.                                 DepType::FF => pred_sched.ef + gap - dur,
  129.                                 DepType::SS => pred_sched.es + gap,
  130.                                 DepType::SF => pred_sched.es + gap - dur,
  131.                             };
  132.                            
  133.                             if candidate_es > max_es {
  134.                                 max_es = candidate_es;
  135.                             }
  136.                         }
  137.                     }
  138.                     max_es
  139.                 } else {
  140.                     project_start
  141.                 };
  142.  
  143.                 let ef = es + dur;
  144.  
  145.                 tmp.insert(*task_id, TmpSchedule {
  146.                     es,
  147.                     ef,
  148.                     ls: deadline,
  149.                     lf: deadline,
  150.                     slack: 0,
  151.                     is_critical: false,
  152.                 });
  153.             }
  154.         }
  155.  
  156.         let reverse_topo: Vec<Id> = topo_order.iter().rev().cloned().collect();
  157.  
  158.         for task_id in &reverse_topo {
  159.             if let Some(&task_idx) = task_by_id.get(task_id) {
  160.                 let task = &tasks[task_idx];
  161.                 let dur = Duration::seconds(task.estimated_duration.unwrap_or(0));
  162.  
  163.                 let lf = if let Some(succ_deps) = successors.get(task_id) {
  164.                     let mut min_lf = deadline;
  165.                    
  166.                     for dep in succ_deps {
  167.                         if let Some(succ_sched) = tmp.get(&dep.to_task_id) {
  168.                             let gap = Duration::seconds(dep.min_gap);
  169.                            
  170.                             let candidate_lf = match dep.dep_type {
  171.                                 DepType::FS => succ_sched.ls - gap,
  172.                                 DepType::FF => succ_sched.lf - gap,
  173.                                 DepType::SS => succ_sched.ls - gap + dur,
  174.                                 DepType::SF => succ_sched.lf - gap + dur,
  175.                             };
  176.                            
  177.                             if candidate_lf < min_lf {
  178.                                 min_lf = candidate_lf;
  179.                             }
  180.                         }
  181.                     }
  182.                     min_lf
  183.                 } else {
  184.                     deadline
  185.                 };
  186.  
  187.                 let ls = lf - dur;
  188.  
  189.                 if let Some(sched) = tmp.get_mut(task_id) {
  190.                     sched.ls = ls;
  191.                     sched.lf = lf;
  192.                 }
  193.             }
  194.         }
  195.  
  196.         for task_id in &topo_order {
  197.             if let Some(sched) = tmp.get_mut(task_id) {
  198.                 let slack_seconds = (sched.lf - sched.ef).num_seconds();
  199.                 sched.slack = slack_seconds.max(0);
  200.                 sched.is_critical = slack_seconds <= 0;
  201.             }
  202.         }
  203.  
  204.         for task in tasks.iter_mut() {
  205.             if let Some(sched) = tmp.get(&task.id) {
  206.                 task.schedule.ls = Some(sched.ls);
  207.                 task.schedule.lf = Some(sched.lf);
  208.                 task.schedule.slack = Some(sched.slack);
  209.                 task.schedule.is_critical = sched.is_critical;
  210.             }
  211.         }
  212.  
  213.         self.aggregate_hierarchy(tasks);
  214.  
  215.         Ok(())
  216.     }
  217.  
  218.     fn aggregate_hierarchy(&self, tasks: &mut [Task]) {
  219.         if tasks.is_empty() {
  220.             return;
  221.         }
  222.  
  223.         let mut by_id: HashMap<Id, usize> = HashMap::new();
  224.         for (idx, task) in tasks.iter().enumerate() {
  225.             by_id.insert(task.id, idx);
  226.         }
  227.  
  228.         let mut children: HashMap<Id, Vec<Id>> = HashMap::new();
  229.         let mut roots: Vec<Id> = Vec::new();
  230.  
  231.         for task in tasks.iter() {
  232.             if let Some(parent_id) = task.parent_task_id {
  233.                 if by_id.contains_key(&parent_id) {
  234.                     children.entry(parent_id).or_default().push(task.id);
  235.                 } else {
  236.                     roots.push(task.id);
  237.                 }
  238.             } else {
  239.                 roots.push(task.id);
  240.             }
  241.         }
  242.  
  243.         for root_id in roots {
  244.             let mut stack: Vec<(Id, usize)> = Vec::new();
  245.             let mut visited: HashSet<Id> = HashSet::new();
  246.            
  247.             stack.push((root_id, 0));
  248.  
  249.             while let Some((task_id, children_processed)) = stack.last_mut() {
  250.                 let current_id = *task_id;
  251.                
  252.                 if visited.contains(&current_id) {
  253.                     stack.pop();
  254.                     continue;
  255.                 }
  256.  
  257.                 let child_ids = children.get(&current_id).map(|v| v.as_slice()).unwrap_or(&[]);
  258.                
  259.                 if *children_processed < child_ids.len() {
  260.                     let child_id = child_ids[*children_processed];
  261.                     *children_processed += 1;
  262.                    
  263.                     if by_id.contains_key(&child_id) {
  264.                         stack.push((child_id, 0));
  265.                     }
  266.                 } else {
  267.                     stack.pop();
  268.                     visited.insert(current_id);
  269.                    
  270.                     if let Some(&task_idx) = by_id.get(&current_id) {
  271.                         let child_ids = children.get(&current_id).map(|v| v.as_slice()).unwrap_or(&[]);
  272.                        
  273.                         if !child_ids.is_empty() {
  274.                             let mut child_ls_values: Vec<DateTime<Utc>> = Vec::new();
  275.                             let mut child_lf_values: Vec<DateTime<Utc>> = Vec::new();
  276.                             let mut child_slack_values: Vec<i64> = Vec::new();
  277.                             let mut any_child_critical = false;
  278.                            
  279.                             for &child_id in child_ids.iter() {
  280.                                 if let Some(&child_idx) = by_id.get(&child_id) {
  281.                                     let child = &tasks[child_idx];
  282.                                     if let Some(ls) = child.schedule.ls {
  283.                                         child_ls_values.push(ls);
  284.                                     }
  285.                                     if let Some(lf) = child.schedule.lf {
  286.                                         child_lf_values.push(lf);
  287.                                     }
  288.                                     if let Some(slack) = child.schedule.slack {
  289.                                         child_slack_values.push(slack);
  290.                                     }
  291.                                     if child.schedule.is_critical {
  292.                                         any_child_critical = true;
  293.                                     }
  294.                                 }
  295.                             }
  296.                            
  297.                             let task = &mut tasks[task_idx];
  298.                            
  299.                             if let Some(&min_child_ls) = child_ls_values.iter().min() {
  300.                                 task.schedule.ls = Some(
  301.                                     task.schedule.ls
  302.                                         .map(|parent_ls| parent_ls.min(min_child_ls))
  303.                                         .unwrap_or(min_child_ls)
  304.                                 );
  305.                             }
  306.                            
  307.                             if let Some(&max_child_lf) = child_lf_values.iter().max() {
  308.                                 task.schedule.lf = Some(
  309.                                     task.schedule.lf
  310.                                         .map(|parent_lf| parent_lf.max(max_child_lf))
  311.                                         .unwrap_or(max_child_lf)
  312.                                 );
  313.                             }
  314.                            
  315.                             if !child_slack_values.is_empty() {
  316.                                 let min_child_slack = *child_slack_values.iter().min().unwrap();
  317.                                 task.schedule.slack = Some(min_child_slack);
  318.                                 task.schedule.is_critical = min_child_slack == 0 || any_child_critical;
  319.                             }
  320.                         }
  321.                     }
  322.                 }
  323.             }
  324.         }
  325.     }
  326. }
  327.  
  328. #[async_trait]
  329. impl ScheduleService for ScheduleServiceImpl {
  330.     async fn reverse_schedule(&self, project_id: Id) -> AppResult<Vec<Task>> {
  331.         let (start_date, due_date) = self.schedule_repo
  332.             .get_project_dates(project_id)
  333.             .await
  334.             .map_err(AppError::Internal)?
  335.             .ok_or_else(|| AppError::NotFound(format!("Project {} not found", project_id)))?;
  336.  
  337.         let mut tasks = self.schedule_repo
  338.             .get_tasks_by_project(project_id)
  339.             .await
  340.             .map_err(AppError::Internal)?;
  341.  
  342.         let task_ids: Vec<Id> = tasks.iter().map(|t| t.id).collect();
  343.        
  344.         let dependencies = self.schedule_repo
  345.             .get_dependencies_by_task_ids(&task_ids)
  346.             .await
  347.             .map_err(AppError::Internal)?;
  348.  
  349.         self.compute_full_schedule(&mut tasks, &dependencies, start_date, due_date)?;
  350.  
  351.         let now = Utc::now();
  352.         for task in &mut tasks {
  353.             task.updated_at = now;
  354.             self.schedule_repo
  355.                 .update_task_schedule(task)
  356.                 .await
  357.                 .map_err(AppError::Internal)?;
  358.         }
  359.  
  360.         Ok(tasks)
  361.     }
  362.  
  363.     async fn compute_schedule(&self, tasks: &[Task]) -> AppResult<Vec<Schedule>> {
  364.         Ok(tasks.iter().map(|t| t.schedule.clone()).collect())
  365.     }
  366. }
  367.  
Tags: rust CMP rdg
Advertisement
Add Comment
Please, Sign In to add comment