diff --git a/.idea/misc.xml b/.idea/misc.xml
index c643c23..08a0d19 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,8 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/entities/CollaborativeTask.java b/src/main/java/entities/CollaborativeTask.java
index a0eae1c..5c62353 100644
--- a/src/main/java/entities/CollaborativeTask.java
+++ b/src/main/java/entities/CollaborativeTask.java
@@ -254,4 +254,6 @@ public void setDeclinedTeammates(ArrayList declinedTeammates) {
this.declinedTeammates = declinedTeammates;
}
+
}
+
diff --git a/src/main/java/scheduling_ct_screens/EnterTimeAndTask.java b/src/main/java/scheduling_ct_screens/EnterTimeAndTask.java
new file mode 100644
index 0000000..172c81e
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/EnterTimeAndTask.java
@@ -0,0 +1,65 @@
+package scheduling_ct_screens;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+/**
+ * The View for the Scheduling Collaborative Tasks Use Case
+ */
+
+public class EnterTimeAndTask extends JFrame implements ActionListener {
+
+ ScheduleCTController scheduleCTController;
+ JTextField taskTitle = new JTextField(15);
+ JTextField startTime = new JTextField(15);
+ JTextField endTime = new JTextField(15);
+
+
+ public EnterTimeAndTask(ScheduleCTController scheduleCTController) {
+
+ this.scheduleCTController = scheduleCTController;
+
+
+ JLabel title = new JLabel("Scheduling Collaborative Tasks");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ LabelTextPanel taskInfo = new LabelTextPanel(new JLabel("Enter task title"), taskTitle);
+
+ LabelTextPanel startInfo = new LabelTextPanel(new JLabel("Enter start time"), startTime);
+
+ LabelTextPanel endInfo = new LabelTextPanel(new JLabel("Enter end time"), endTime);
+
+
+ JButton submit = new JButton("Submit");
+
+ JPanel buttons = new JPanel();
+ buttons.add(submit);
+
+ submit.addActionListener(this);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ JPanel main = new JPanel();
+ main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
+
+ main.add(title);
+ main.add(taskInfo);
+ main.add(startInfo);
+ main.add(endInfo);
+ main.add(buttons);
+ this.setContentPane(main);
+
+ this.pack();
+ setVisible(true);
+ }
+
+ // React to button click that results in evt
+ public void actionPerformed(ActionEvent evt) {
+// System.out.println("Click" + evt.getActionCommand());
+ scheduleCTController.isConflict(taskTitle.getText(), startTime.getText(), endTime.getText());
+
+
+ }
+}
diff --git a/src/main/java/scheduling_ct_screens/LabelTextPanel.java b/src/main/java/scheduling_ct_screens/LabelTextPanel.java
new file mode 100644
index 0000000..d6dc19a
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/LabelTextPanel.java
@@ -0,0 +1,16 @@
+package scheduling_ct_screens;
+
+import javax.swing.*;
+
+// Frameworks/Drivers layer
+
+/**
+ * UI helper class that creates labeled text panel
+ */
+
+public class LabelTextPanel extends JPanel {
+ public LabelTextPanel(JLabel label, JTextField textField) {
+ this.add(label);
+ this.add(textField);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/scheduling_ct_screens/PresentOutput.java b/src/main/java/scheduling_ct_screens/PresentOutput.java
new file mode 100644
index 0000000..609433d
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/PresentOutput.java
@@ -0,0 +1,20 @@
+package scheduling_ct_screens;
+
+import javax.swing.*;
+
+public class PresentOutput extends JFrame {
+
+ String message;
+
+ public PresentOutput(String message) {
+ this.message = message;
+
+ JLabel text = new JLabel(message);
+
+ JPanel main = new JPanel();
+ main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
+
+ main.add(text);
+ setVisible(true);
+ }
+}
diff --git a/src/main/java/scheduling_ct_screens/ScheduleCTController.java b/src/main/java/scheduling_ct_screens/ScheduleCTController.java
new file mode 100644
index 0000000..d0f576a
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/ScheduleCTController.java
@@ -0,0 +1,40 @@
+package scheduling_ct_screens;
+import entities.StudentUser;
+import entities.Task;
+import scheduling_ct_use_case.*;
+
+import java.util.HashMap;
+
+/**
+ * Controller for the Scheduling Collaborative Tasks Use Case
+ * Triggers the interactor
+ */
+
+public class ScheduleCTController {
+
+ final ScheduleCTInputBoundary scheduleInput;
+
+ private final HashMap hashMap;
+
+ private final StudentUser studentUser;
+
+ public ScheduleCTController(ScheduleCTInputBoundary scheduleInput, HashMap hashMap, StudentUser studentUser) {
+ this.scheduleInput = scheduleInput;
+ this.hashMap = hashMap;
+ this.studentUser = studentUser;
+ }
+
+ public HashMap getTaskMap() {
+ return hashMap;
+ }
+
+ public StudentUser getStudentUser() {
+ return studentUser;
+ }
+
+ public ScheduleCTResponseModel isConflict(String taskName, String startTime, String endTime) {
+ ScheduleCTRequestModel inputData = new ScheduleCTRequestModel(taskName, startTime, endTime, studentUser);
+ return scheduleInput.schedule(inputData, this.hashMap);
+ }
+
+}
diff --git a/src/main/java/scheduling_ct_screens/ScheduleCTPresenter.java b/src/main/java/scheduling_ct_screens/ScheduleCTPresenter.java
new file mode 100644
index 0000000..a5e0104
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/ScheduleCTPresenter.java
@@ -0,0 +1,23 @@
+package scheduling_ct_screens;
+import scheduling_ct_use_case.*;
+
+/**
+ * Presenter for the Collaborative Scheduling Use Case
+ * Implements the Output Boundary as part of the dependency inversion
+ */
+
+public class ScheduleCTPresenter implements ScheduleCTOutputBoundary {
+
+ @Override
+ public ScheduleCTResponseModel prepareNoConflictView(ScheduleCTResponseModel responseModel) {
+ if ((!responseModel.getIsConflict())) {
+ responseModel.setDisplayString("Successful input");
+ }
+ return responseModel;
+ }
+
+ @Override
+ public ScheduleCTResponseModel prepareFailView(String error) {
+ throw new SchedulingTimesFailed(error);
+ }
+}
diff --git a/src/main/java/scheduling_ct_screens/SchedulingTimesFailed.java b/src/main/java/scheduling_ct_screens/SchedulingTimesFailed.java
new file mode 100644
index 0000000..758bd09
--- /dev/null
+++ b/src/main/java/scheduling_ct_screens/SchedulingTimesFailed.java
@@ -0,0 +1,7 @@
+package scheduling_ct_screens;
+
+public class SchedulingTimesFailed extends RuntimeException{
+ public SchedulingTimesFailed(String error){
+ super(error);
+ }
+}
diff --git a/src/main/java/scheduling_ct_use_case/ScheduleCTInputBoundary.java b/src/main/java/scheduling_ct_use_case/ScheduleCTInputBoundary.java
new file mode 100644
index 0000000..12b8365
--- /dev/null
+++ b/src/main/java/scheduling_ct_use_case/ScheduleCTInputBoundary.java
@@ -0,0 +1,14 @@
+package scheduling_ct_use_case;
+
+import entities.Task;
+import java.util.HashMap;
+
+/**
+ * Input Boundary interface for the Scheduling Collaborative Tasks Use Case
+ * (inverts dependency from Controller to Interactor)
+ */
+
+public interface ScheduleCTInputBoundary {
+
+ ScheduleCTResponseModel schedule(ScheduleCTRequestModel scheduleCTRequestModel, HashMap hashMap);
+}
diff --git a/src/main/java/scheduling_ct_use_case/ScheduleCTInteractor.java b/src/main/java/scheduling_ct_use_case/ScheduleCTInteractor.java
new file mode 100644
index 0000000..b959a12
--- /dev/null
+++ b/src/main/java/scheduling_ct_use_case/ScheduleCTInteractor.java
@@ -0,0 +1,304 @@
+package scheduling_ct_use_case;
+
+import entities.*;
+
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Scheduling Collaborative Tasks Use Case Interactor (use case layer)
+ * Implements business logic on entities
+ */
+
+public class ScheduleCTInteractor implements ScheduleCTInputBoundary {
+ private final ScheduleCTOutputBoundary scheduleCTOutputBoundary;
+
+ public ScheduleCTInteractor(ScheduleCTOutputBoundary scheduleCTOutputBoundary) {
+ this.scheduleCTOutputBoundary = scheduleCTOutputBoundary;
+ }
+
+ /**
+ * The main controller of this interactor that calls the helper methods
+ */
+ @Override
+ public ScheduleCTResponseModel schedule(ScheduleCTRequestModel requestModel, HashMap hashMap) {
+ // returns that this has conflict
+ // otherwise will automatically schedule and return a success view
+
+
+ CollaborativeTask task = getTaskObjectFromName(requestModel.getTaskName(), hashMap);
+
+ if (requestModel.getStudentUser() != task.getLeader()) {
+ return scheduleCTOutputBoundary.prepareFailView("User is not the leader. " +
+ "You don't have scheduling access");
+ }
+
+ ArrayList users = task.getTeammates();
+ users.add(task.getLeader());
+
+ ArrayList unavailableUsers = new ArrayList<>();
+
+ LocalDateTime startTime = convertStringToLocalDateTime(requestModel.getStartTime());
+ LocalDateTime endTime = convertStringToLocalDateTime(requestModel.getEndTime());
+
+ for (StudentUser user : users) {
+ ArrayList userTasks = getTaskFromId(user, hashMap);
+ if (!isUserAvailableAtDateTime(user, userTasks, startTime, endTime)) {
+ unavailableUsers.add(user);
+ }
+ }
+ if (!unavailableUsers.isEmpty()) {
+ if (task.getFrequency().equals("")) {
+ ArrayList> oneDate = new ArrayList<>();
+ ArrayList theDate = new ArrayList<>();
+ theDate.add(startTime);
+ theDate.add(endTime);
+ oneDate.add(theDate);
+ task.setTimeBlocks(oneDate);
+ } else {
+ ArrayList> dates = getDates(task.getFrequency(), startTime, endTime, task.getDeadline());
+ task.setTimeBlocks(dates);
+ }
+ ScheduleCTResponseModel scheduleCTResponseModel = new ScheduleCTResponseModel(false);
+ return scheduleCTOutputBoundary.prepareNoConflictView(scheduleCTResponseModel);
+ } else {
+ ScheduleCTResponseModel scheduleCTResponseModel = new ScheduleCTResponseModel(true);
+ return scheduleCTOutputBoundary.prepareFailView("Cannot schedule due to conflict");
+ }
+ }
+
+ /**
+ * Finds all the dates to meet up given the frequency of a task
+ * @param frequency - either "daily", "weekly" or "monthly"
+ * @param startTime - the start time of the time block
+ * @param endTime - the end time of the time block
+ * @param deadline - the deadline of the task
+ * @return an array list of array lists of local date time (i.e. the dates to meet up)
+ */
+ public ArrayList> getDates(String frequency, LocalDateTime startTime, LocalDateTime endTime, LocalDateTime deadline) {
+ ArrayList> times = new ArrayList<>();
+ ArrayList initialTime = new ArrayList<>();
+ initialTime.add(startTime);
+ initialTime.add(endTime);
+ times.add(initialTime);
+
+ LocalDateTime currStart = startTime;
+ LocalDateTime currEnd = endTime;
+
+ switch (frequency) {
+ case "daily":
+ do {
+ ArrayList date = new ArrayList<>();
+ currStart = currStart.plusDays(1);
+ currEnd = currEnd.plusDays(1);
+
+ date.add(currStart);
+ date.add(currEnd);
+ times.add(date);
+ } while (endTime.isBefore(deadline));
+ break;
+ case "weekly":
+ do {
+ ArrayList date = new ArrayList<>();
+ currStart = currStart.plusWeeks(1);
+ currEnd = currEnd.plusWeeks(1);
+
+ date.add(currStart);
+ date.add(currEnd);
+ times.add(date);
+ } while (endTime.isBefore(deadline));
+ break;
+ case "monthly":
+ do {
+ ArrayList date = new ArrayList<>();
+ currStart = currStart.plusMonths(1);
+ currEnd = currEnd.plusMonths(1);
+
+ date.add(currStart);
+ date.add(currEnd);
+ times.add(date);
+ } while (endTime.isBefore(deadline));
+ break;
+ }
+ return times;
+ }
+
+ /**
+ * Check if a user is available at a fixed date time
+ * @param user - the student user
+ * @param tasks - an array list of a user's tasks
+ * @param start - the start time of the time block
+ * @param end -the end time of the time block
+ * @return whether or not the user is available at this date time
+ */
+ public boolean isUserAvailableAtDateTime(StudentUser user, ArrayList tasks, LocalDateTime start,
+ LocalDateTime end) {
+ // assume there's a method in TaskUseCase that gets all the tasks a student has
+
+ boolean is_task_free = true;
+ boolean is_working_hours_free = true;
+ for (Task task : tasks) {
+
+ if (task instanceof Event) {
+ if (((Event) task).getTimeBlock() != null) {
+ LocalDateTime[] timeBlock = ((Event) task).getTimeBlock();
+ LocalDateTime timeBlockStart = timeBlock[0];
+ LocalDateTime timeBlockEnd = timeBlock[1];
+ is_task_free = is_task_free && givenTime(timeBlockStart, timeBlockEnd, start, end);
+ }
+ } else if (task instanceof Test) {
+ if (((Test) task).getTimeBlock() != null) {
+
+ LocalDateTime[] timeBlock = ((Test) task).getTimeBlock();
+ LocalDateTime timeBlockStart = timeBlock[0];
+ LocalDateTime timeBlockEnd = timeBlock[1];
+
+ is_task_free = is_task_free && givenTime(timeBlockStart, timeBlockEnd, start, end);
+
+ if (((Test) task).getPrepTimeScheduled() != null) {
+ ArrayList> scheduledPrep = ((Test) task).getPrepTimeScheduled();
+ for (ArrayList prep : scheduledPrep) {
+ LocalDateTime prepStart = prep.get(0);
+ LocalDateTime prepEnd = prep.get(1);
+
+ is_task_free = is_task_free && givenTime(prepStart, prepEnd, start, end);
+ }
+
+ }
+ }
+ } else if (task instanceof Assignment) {
+
+ if (((Assignment) task).getPrepTimeScheduled() != null) {
+ ArrayList> scheduledPrep = ((Assignment) task).getPrepTimeScheduled();
+ for (ArrayList prep : scheduledPrep) {
+ LocalDateTime prepStart = prep.get(0);
+ LocalDateTime prepEnd = prep.get(1);
+
+ is_task_free = is_task_free && givenTime(prepStart, prepEnd, start, end);
+ }
+ }
+ }
+
+ }
+ if (user.getWorkingHours() != null) {
+ ArrayList workingHours = user.getWorkingHours();
+ LocalTime workingHoursStart = workingHours.get(0);
+ LocalTime workingHoursEnd = workingHours.get(1);
+ is_working_hours_free = workingHoursFree(start, end, workingHoursStart, workingHoursEnd);
+ }
+ return is_task_free && is_working_hours_free;
+ }
+
+ /**
+ * Check if a time block conflicts with a user's working hours
+ * @param timeBlockStart - the start of the time block
+ * @param timeBlockEnd - the end of the time block
+ * @param workingHoursStart - the start of a user's working hours
+ * @param workingHoursEnd - the end of a user's working hours
+ * @return whether or not a time block conflicts with a user's working hours
+ */
+ public boolean workingHoursFree(LocalDateTime timeBlockStart, LocalDateTime timeBlockEnd,
+ LocalTime workingHoursStart, LocalTime workingHoursEnd) {
+ // if timeBlock is within working hours
+ // timeBlockStart.isAfter(ChronoLocalDateTime.from(workingHoursStart)) &&
+ // timeBlockEnd.isBefore(ChronoLocalDateTime.from(workingHoursEnd))
+ if (timeBlockStart.getHour() > workingHoursStart.getHour() &&
+ timeBlockEnd.getHour() < workingHoursEnd.getHour()) {
+ return false;
+ // if timeBlock covers the whole period of working hours
+ // timeBlockStart.isBefore(ChronoLocalDateTime.from(workingHoursStart)) &&
+ // timeBlockEnd.isAfter(ChronoLocalDateTime.from(workingHoursEnd))
+ } else if (timeBlockStart.getHour() < workingHoursStart.getHour() &&
+ timeBlockEnd.getHour() > workingHoursEnd.getHour()) {
+ return false;
+ // if timeBlockStart is before workingHoursStart and timeBlockEnd is before workingHoursEnd
+ } else if (timeBlockStart.getHour()< workingHoursStart.getHour() &&
+ timeBlockEnd.getHour() < workingHoursEnd.getHour()) {
+ return false;
+ // if timeBlockStart is the same as workingHoursStart
+ } else if (timeBlockStart.getHour() == workingHoursStart.getHour()) {
+ return false;
+ // if timeBlockEnd is the same as workingHoursEnd
+ } else if (timeBlockEnd.getHour() == workingHoursEnd.getHour()) {
+ return false;
+ // if timeBlockStart is after workingHoursStart and timeBlockEnd is after workingHoursEnd
+ } else return !(timeBlockStart.getHour() > workingHoursStart.getHour() &&
+ timeBlockEnd.getHour() > workingHoursEnd.getHour());
+ }
+
+ /**
+ * Check if two time blocks conflict
+ * @param timeBlockStart - the start of the time block
+ * @param timeBlockEnd - the end of the time block
+ * @param start - the start of another time block
+ * @param end - the end of another time block
+ * @return whether or not two time blocks conflict
+ */
+ public boolean givenTime(LocalDateTime timeBlockStart, LocalDateTime timeBlockEnd,
+ LocalDateTime start, LocalDateTime end) {
+ // in the case where the time block is within the time of start and end
+ if (timeBlockStart.isAfter(start) && timeBlockEnd.isBefore(end)) {
+ return false;
+ // in the case where it covers the time period
+ } else if (timeBlockStart.isBefore(start) && timeBlockEnd.isAfter(end)) {
+ return false;
+ // in the case where timeBlockStart is before start and timeBlockEnd is before end
+ } else if (timeBlockStart.isBefore(start) && timeBlockEnd.isBefore(end)) {
+ return false;
+ // if the start time same as start time of block, return false
+ } else if (timeBlockStart.isEqual(start)) {
+ return false;
+ // if the end time same as end time of block, return false
+ } else if (timeBlockEnd.isEqual(end)) {
+ return false;
+ // in the case where timeBlockStart is after start time and timeBlockEnd is after end time
+ } else return !(timeBlockStart.isAfter(start) && timeBlockEnd.isAfter(end));
+ }
+
+ /**
+ * Retrieve the tasks associated with a user from the given hash map
+ * @param user - the student user
+ * @param hashMap - a hash map of all task ids to tasks
+ * @return an array list of tasks
+ */
+ public ArrayList getTaskFromId(StudentUser user, HashMap hashMap) {
+ ArrayList userTasks = new ArrayList<>();
+
+ ArrayList toDoList = user.getToDoList();
+
+ for (String taskId : toDoList) {
+ Task task_value = hashMap.get(taskId);
+ userTasks.add(task_value);
+ }
+ return userTasks;
+ }
+
+ /**
+ * Get the task object from the task map given the name of the task
+ * @param taskName - the name of the task
+ * @param hashMap - hashmap of task ids to tasks
+ * @return the task object that corresponds to the task name
+ */
+ public CollaborativeTask getTaskObjectFromName(String taskName, HashMap hashMap) {
+ for (Task task : hashMap.values()) {
+ if (task.getTitle().equals(taskName) && task instanceof CollaborativeTask) {
+ return (CollaborativeTask) task;
+ }
+ }
+ throw new RuntimeException("Task does not exist");
+ }
+
+ /**
+ * Convert the given string to a LocalDateTime object
+ * @param stringTime - the string representation of a date time
+ * @return the string formatted as LocalDateTime
+ */
+ public LocalDateTime convertStringToLocalDateTime(String stringTime) {
+ DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+ return LocalDateTime.parse(stringTime, format);
+ }
+
+}
diff --git a/src/main/java/scheduling_ct_use_case/ScheduleCTOutputBoundary.java b/src/main/java/scheduling_ct_use_case/ScheduleCTOutputBoundary.java
new file mode 100644
index 0000000..03c9ca0
--- /dev/null
+++ b/src/main/java/scheduling_ct_use_case/ScheduleCTOutputBoundary.java
@@ -0,0 +1,13 @@
+package scheduling_ct_use_case;
+
+/**
+ * Output Boundary for the Scheduling Collaborative Tasks Use Case
+ * (inverts dependency for interactor to presenter)
+ */
+
+public interface ScheduleCTOutputBoundary {
+
+ ScheduleCTResponseModel prepareNoConflictView(ScheduleCTResponseModel responseModel);
+
+ ScheduleCTResponseModel prepareFailView(String error);
+}
diff --git a/src/main/java/scheduling_ct_use_case/ScheduleCTRequestModel.java b/src/main/java/scheduling_ct_use_case/ScheduleCTRequestModel.java
new file mode 100644
index 0000000..7a09f6a
--- /dev/null
+++ b/src/main/java/scheduling_ct_use_case/ScheduleCTRequestModel.java
@@ -0,0 +1,42 @@
+package scheduling_ct_use_case;
+
+import entities.StudentUser;
+
+/**
+ * Request Model for the Scheduling Collaborative Tasks Use Case
+ * Acts as the input data object in the use case layer
+ */
+
+public class ScheduleCTRequestModel {
+
+ private final String taskName;
+
+ private final String startTime;
+
+ private final String endTime;
+
+ private final StudentUser studentUser;
+
+ public ScheduleCTRequestModel(String taskName, String startTime, String endTime, StudentUser studentUser) {
+ this.taskName = taskName;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.studentUser = studentUser;
+ }
+
+ public String getTaskName() {
+ return taskName;
+ }
+ public String getStartTime() {
+ return startTime;
+ }
+ public String getEndTime() {
+ return endTime;
+ }
+
+ public StudentUser getStudentUser() {
+ return studentUser;
+ }
+
+
+}
diff --git a/src/main/java/scheduling_ct_use_case/ScheduleCTResponseModel.java b/src/main/java/scheduling_ct_use_case/ScheduleCTResponseModel.java
new file mode 100644
index 0000000..17c4b24
--- /dev/null
+++ b/src/main/java/scheduling_ct_use_case/ScheduleCTResponseModel.java
@@ -0,0 +1,28 @@
+package scheduling_ct_use_case;
+
+/**
+ * Response Model for the Scheduling Collaborative Tasks Use Case
+ * Acts as the output data object in the use case layer
+ */
+
+public class ScheduleCTResponseModel {
+
+ private final boolean isConflict;
+ String displayString;
+
+ public ScheduleCTResponseModel(boolean isConflict) {
+
+ this.isConflict = isConflict;
+ }
+
+ public boolean getIsConflict() {
+ return isConflict;
+ }
+
+ public String getDisplayString() {
+ return displayString;
+ }
+ public void setDisplayString(String displayString) {
+ this.displayString = displayString;
+ }
+}