Home > other >  How to calculate start and finish dates of the task that depends on previous tasks to start
How to calculate start and finish dates of the task that depends on previous tasks to start

Time:07-15

I need to create an endpoint that returns the task list with the start and end date, but tasks that depend on another task to start do not have dates recorded in the database, so I need to create this task list by calculating the start and end based on previous tasks.

Can you tell me how to implement this?

Below is the current answer and the answer I hope to get with your help!

Current response:

[
    {
        "id": 1,
        "name": "Analysis of Requirement",
        "isDependent": false,
        "dependencies": [],
        "durationDays": 30,
        "startDate": "2022-08-01",
        "endDate": "2022-08-31"
    },
    {
        "id": 2,
        "name": "Product Design",
        "isDependent": true,
        "dependencies": [
            {
                "id": 1,
                "taskDependency": {
                    "id": 1,
                    "name": "Analysis of Requirement",
                    "isDependent": false,
                    "dependencies": [],
                    "durationDays": 30,
                    "startDate": "2022-08-01",
                    "endDate": "2022-08-31"
                }
            }
        ],
        "durationDays": 30,
        "startDate": null,
        "endDate": null
    },
    {
        "id": 3,
        "name": "Coding",
        "isDependent": true,
        "dependencies": [
            {
                "id": 2,
                "taskDependency": {
                    "id": 2,
                    "name": "Product Design",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 1,
                            "taskDependency": {
                                "id": 1,
                                "name": "Analysis of Requirement",
                                "isDependent": false,
                                "dependencies": [],
                                "durationDays": 30,
                                "startDate": "2022-08-01",
                                "endDate": "2022-08-31"
                            }
                        }
                    ],
                    "durationDays": 30,
                    "startDate": null,
                    "endDate": null
                }
            }
        ],
        "durationDays": 120,
        "startDate": null,
        "endDate": null
    },
    {
        "id": 4,
        "name": "Testing",
        "isDependent": true,
        "dependencies": [
            {
                "id": 3,
                "taskDependency": {
                    "id": 3,
                    "name": "Coding",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 2,
                            "taskDependency": {
                                "id": 2,
                                "name": "Product Design",
                                "isDependent": true,
                                "dependencies": [
                                    {
                                        "id": 1,
                                        "taskDependency": {
                                            "id": 1,
                                            "name": "Analysis of Requirement",
                                            "isDependent": false,
                                            "dependencies": [],
                                            "durationDays": 30,
                                            "startDate": "2022-08-01",
                                            "endDate": "2022-08-31"
                                        }
                                    }
                                ],
                                "durationDays": 30,
                                "startDate": null,
                                "endDate": null
                            }
                        }
                    ],
                    "durationDays": 120,
                    "startDate": null,
                    "endDate": null
                }
            }
        ],
        "durationDays": 30,
        "startDate": null,
        "endDate": null
    },
    {
        "id": 5,
        "name": "Deployment",
        "isDependent": true,
        "dependencies": [
            {
                "id": 4,
                "taskDependency": {
                    "id": 4,
                    "name": "Testing",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 3,
                            "taskDependency": {
                                "id": 3,
                                "name": "Coding",
                                "isDependent": true,
                                "dependencies": [
                                    {
                                        "id": 2,
                                        "taskDependency": {
                                            "id": 2,
                                            "name": "Product Design",
                                            "isDependent": true,
                                            "dependencies": [
                                                {
                                                    "id": 1,
                                                    "taskDependency": {
                                                        "id": 1,
                                                        "name": "Analysis of Requirement",
                                                        "isDependent": false,
                                                        "dependencies": [],
                                                        "durationDays": 30,
                                                        "startDate": "2022-08-01",
                                                        "endDate": "2022-08-31"
                                                    }
                                                }
                                            ],
                                            "durationDays": 30,
                                            "startDate": null,
                                            "endDate": null
                                        }
                                    }
                                ],
                                "durationDays": 120,
                                "startDate": null,
                                "endDate": null
                            }
                        }
                    ],
                    "durationDays": 30,
                    "startDate": null,
                    "endDate": null
                }
            }
        ],
        "durationDays": 30,
        "startDate": null,
        "endDate": null
    }
]

Expected response:

[
    {
        "id": 1,
        "name": "Analysis of Requirement",
        "isDependent": false,
        "dependencies": [],
        "durationDays": 30,
        "startDate": "2022-08-01",
        "endDate": "2022-08-31"
    },
    {
        "id": 2,
        "name": "Product Design",
        "isDependent": true,
        "dependencies": [
            {
                "id": 1,
                "taskDependency": {
                    "id": 1,
                    "name": "Analysis of Requirement",
                    "isDependent": false,
                    "dependencies": [],
                    "durationDays": 30,
                    "startDate": "2022-08-01",
                    "endDate": "2022-08-31"
                }
            }
        ],
        "durationDays": 30,
        "startDate": "2022-08-31",
        "endDate": "2022-09-30"
    },
    {
        "id": 3,
        "name": "Coding",
        "isDependent": true,
        "dependencies": [
            {
                "id": 2,
                "taskDependency": {
                    "id": 2,
                    "name": "Product Design",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 1,
                            "taskDependency": {
                                "id": 1,
                                "name": "Analysis of Requirement",
                                "isDependent": false,
                                "dependencies": [],
                                "durationDays": 30,
                                "startDate": "2022-08-01",
                                "endDate": "2022-08-31"
                            }
                        }
                    ],
                    "durationDays": 30,
                    "startDate": "2022-08-31",
                    "endDate": "2022-09-30"
                }
            }
        ],
        "durationDays": 120,
        "startDate": "2022-09-30",
        "endDate": "2023-01-28"
    },
    {
        "id": 4,
        "name": "Testing",
        "isDependent": true,
        "dependencies": [
            {
                "id": 3,
                "taskDependency": {
                    "id": 3,
                    "name": "Coding",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 2,
                            "taskDependency": {
                                "id": 2,
                                "name": "Product Design",
                                "isDependent": true,
                                "dependencies": [
                                    {
                                        "id": 1,
                                        "taskDependency": {
                                            "id": 1,
                                            "name": "Analysis of Requirement",
                                            "isDependent": false,
                                            "dependencies": [],
                                            "durationDays": 30,
                                            "startDate": "2022-08-01",
                                            "endDate": "2022-08-31"
                                        }
                                    }
                                ],
                                "durationDays": 30,
                                "startDate": "2022-08-31",
                                "endDate": "2022-09-30"
                            }
                        }
                    ],
                    "durationDays": 120,
                    "startDate": "2022-09-30",
                    "endDate": "2023-01-28"
                }
            }
        ],
        "durationDays": 30,
        "startDate": "2023-01-28",
        "endDate": "2023-02-27"
    },
    {
        "id": 5,
        "name": "Deployment",
        "isDependent": true,
        "dependencies": [
            {
                "id": 4,
                "taskDependency": {
                    "id": 4,
                    "name": "Testing",
                    "isDependent": true,
                    "dependencies": [
                        {
                            "id": 3,
                            "taskDependency": {
                                "id": 3,
                                "name": "Coding",
                                "isDependent": true,
                                "dependencies": [
                                    {
                                        "id": 2,
                                        "taskDependency": {
                                            "id": 2,
                                            "name": "Product Design",
                                            "isDependent": true,
                                            "dependencies": [
                                                {
                                                    "id": 1,
                                                    "taskDependency": {
                                                        "id": 1,
                                                        "name": "Analysis of Requirement",
                                                        "isDependent": false,
                                                        "dependencies": [],
                                                        "durationDays": 30,
                                                        "startDate": "2022-08-01",
                                                        "endDate": "2022-08-31"
                                                    }
                                                }
                                            ],
                                            "durationDays": 30,
                                            "startDate": "2022-08-31",
                                            "endDate": "2022-09-30"
                                        }
                                    }
                                ],
                                "durationDays": 120,
                                "startDate": "2022-09-30",
                                "endDate": "2023-01-28"
                            }
                        }
                    ],
                    "durationDays": 30,
                    "startDate": "2023-01-28",
                    "endDate": "2023-02-27"
                }
            }
        ],
        "durationDays": 30,
        "startDate": "2023-02-27",
        "endDate": "2023-03-29"
    }
]

Requirements:

  1. To simplify the task will have only one previous task as a dependency.
  2. The finish date of the previous task is the start date of the current task.
  3. To calculate the dates, you need to go through all the dependent tasks until you reach the task where the isDependent field is set to false.

My classes:

MODEL

Task

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.*;

import javax.persistence.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "tasks")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    private Boolean isDependent;

    @EqualsAndHashCode.Exclude
    @OneToMany(mappedBy = "task",
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            fetch = FetchType.LAZY)
    @JsonManagedReference
    private List<TaskDependency> dependencies = new ArrayList<>();

    private Integer durationDays;
    private LocalDate startDate;
    private LocalDate endDate;

}

TaskDependency

package com.example.demo.model;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "task_dependencies")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class TaskDependency {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "task_id", nullable = false
            , foreignKey = @ForeignKey(name = "fk_task_dependency_task1"))
    @JsonBackReference
    private Task task;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dependency_id", nullable = false
            , foreignKey = @ForeignKey(name = "fk_task_dependency_task2"))
    private Task taskDependency;

}

DTO

TaskPost

package com.example.demo.dto;

import com.example.demo.model.TaskDependency;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TaskPost {

    private String name;
    private Boolean isDependent;
    private List<TaskDependency> dependencies = new ArrayList<>();
    private Integer durationDays;
    private LocalDate startDate;

}

MAPPER

TaskMapper

package com.example.demo.mapper;

import com.example.demo.dto.TaskPost;
import com.example.demo.model.Task;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "spring")
public abstract class TaskMapper {

    public static final TaskMapper INSTANCE = Mappers.getMapper(TaskMapper.class);

    public abstract Task toTask(TaskPost taskPost);

}

REPOSITORY

TaskRep

package com.example.demo.repository;

import com.example.demo.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TaskRep extends JpaRepository<Task, Long> {

}

SERVICE

TaskService

package com.example.demo.service;

import com.example.demo.dto.TaskPost;
import com.example.demo.mapper.TaskMapper;
import com.example.demo.model.Task;
import com.example.demo.model.TaskDependency;
import com.example.demo.repository.TaskRep;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class TaskService {

    private final TaskRep taskRep;

    public List<Task> findAll() {
        return taskRep.findAll();
    }

    @Transactional
    public Task save(TaskPost taskPost) {
        Task task = TaskMapper.INSTANCE.toTask(taskPost);
        List<TaskDependency> taskDependencies = new ArrayList<>(task.getDependencies());
        task.setDependencies(taskDependencies);

        if (!task.getIsDependent()) {
            LocalDate startDate = LocalDate.parse(String.valueOf(task.getStartDate()));
            LocalDate endDate = startDate.plusDays(task.getDurationDays());
            task.setEndDate(endDate);
        }

        task.getDependencies().forEach((child) -> child.setTask(task));

        return taskRep.save(task);
    }

}

CONTROLLER

TaskController

package com.example.demo.controller;

import com.example.demo.dto.TaskPost;
import com.example.demo.model.Task;
import com.example.demo.service.TaskService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RestController
@RequestMapping("api/v1/tasks")
@RequiredArgsConstructor
public class TaskController {

    private final TaskService taskService;

    @GetMapping
    public ResponseEntity<List<Task>> findAll() {
        return ResponseEntity.ok(taskService.findAll());
    }

    @PostMapping
    public ResponseEntity<Task> save(@NonNull @Valid @RequestBody TaskPost taskPost) {
        return new ResponseEntity<>(taskService.save(taskPost), HttpStatus.CREATED);
    }

}

CodePudding user response:

I would have an algorithm like this:

  1. read in only the root level of tasks, with only the IDs of the dependent tasks. The other information of the nested tasks looks pretty redundant to me. Read them into a map with the task ID as key, and task as value.
  2. process the entryset of the map once to set the predecessor of each task once as object reference. In this step put all tasks into a new Queue, because after that step you won't need the map anylonger, but might need to process entries multiple times. Or you stick to the entryset for further processing.
  3. now process the queue of all tasks: take the first entry, see if it has a start time set, and a predecessor.
    • If it has set none of them, report an error (you can't calculate times then).
    • If it has no predecessor, work done. Put to a "processed" set for output.
    • If it has a start time, work done. Put to a "processed" set for output.
    • If none of the above, see if the predecessor has an end time set.
      • If yes, calculuate your own start and end time and put to a "processed" set for output.
      • If no, add entry to the queue again.

Process queue entries as long as the queue is not empty. This should save you from doing recursive processing.

For the output, you might need to recreate the model, or if you designed it carefully you just can serialize the "processed" set.

UPDATE: short demo code per request - only the algo, not the handling of requests or JSON parsing or writing.

Please note that just for the sake of brevity I skipped the getters and setters on the Task class, I do not recommend to do so in "real" code.

Task.class:

package examples.stackoverflow.q72967644;

import java.time.LocalDate;
import java.util.Objects;

class Task {
    final Long id;
    final String name;
    Long dependsOn;
    final Integer durationDays;
    LocalDate startDate;
    Task depends;

    @Override
    public String toString() {
        return "Task{"  
                "id="   id  
                ", name='"   name   '\''  
                ", dependsOn="   dependsOn  
                ", durationDays="   durationDays  
                ", startDate="   startDate  
                '}';
    }

    public Task(Long id, String name, Integer durationDays, Long dependsOn) {
        this.id = id;
        this.name = name;
        this.durationDays = durationDays;
        this.dependsOn = dependsOn;
    }

    public Task(Long id, String name, Integer durationDays, LocalDate startDate) {
        this.id = id;
        this.name = name;
        this.durationDays = durationDays;
        this.startDate = startDate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Task task = (Task) o;
        return Objects.equals(id, task.id);
    }

    @Override
    public int hashCode() {
        return id != null ? id.hashCode() : 0;
    }
}

Q72967644.class:

package examples.stackoverflow.q72967644;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

public class Q72967644 {

    public static void main(String[] args) {
        Q72967644 tests = new Q72967644();
        List<Task> tasks = tests.init();
        tests.calculateDates(tasks);
        tasks.forEach(t->System.out.println(t));
    }

    private List<Task> init() {
        List<Task> tasks = new ArrayList<>(5);
        tasks.add(new Task(1L, "Analysis of Requirement", 30, LocalDate.of(2022, 8, 1)));
        tasks.add(new Task(2L, "Product Design", 30, 1L));
        tasks.add(new Task(3L, "Coding", 120, 2L));
        tasks.add(new Task(4L, "Testing", 30, 3L));
        tasks.add(new Task(5L, "Deployment", 30, 4L));
        return tasks;
    }

    private void calculateDates(List<Task> tasks) {
        // Step 1: read into a map
        Map<Long, Task> taskById = tasks.stream().collect(Collectors.toMap(t->t.id, t->t));

        // Step 2: resolve dependencies
        Queue<Task> processing = new LinkedList<>(tasks);
        processing.forEach(task -> task.depends = taskById.get(task.dependsOn));

        // Step 3: calculate
        while (!processing.isEmpty()) {
            Task task = processing.poll();
            if (task.startDate == null) {
                final Task predecessor = task.depends;
                if (predecessor == null) {
                    throw new IllegalStateException("task found with no start time and no predecessor");
                }
                if (predecessor.startDate == null) {
                    processing.add(task);
                } else {
                    task.startDate = predecessor.startDate.plusDays(predecessor.durationDays);
                }
            }
        }
    }
}
  • Related