Skip to content

Commit 5275d7d

Browse files
Merge pull request nus-cs2103-AY2021S1#74 from nicholasyeo/branch-timetable-storage
Allow fitNUS to save and load timetable slots
2 parents 643120f + 573a381 commit 5275d7d

File tree

13 files changed

+280
-7
lines changed

13 files changed

+280
-7
lines changed

src/main/java/seedu/address/logic/Logic.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import seedu.address.model.person.Lesson;
1313
import seedu.address.model.person.Person;
1414
import seedu.address.model.person.Routine;
15+
import seedu.address.model.person.Slot;
1516

1617
/**
1718
* API of the Logic component
@@ -45,6 +46,9 @@ public interface Logic {
4546
/** Returns an unmodifiable view of the filtered list of lessons */
4647
ObservableList<Lesson> getFilteredLessonList();
4748

49+
/** Returns an unmodifiable view of the filtered list of slots */
50+
ObservableList<Slot> getFilteredSlotList();
51+
4852
/**
4953
* Returns the user prefs' address book file path.
5054
*/

src/main/java/seedu/address/logic/LogicManager.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import seedu.address.model.person.Lesson;
1919
import seedu.address.model.person.Person;
2020
import seedu.address.model.person.Routine;
21+
import seedu.address.model.person.Slot;
2122
import seedu.address.storage.Storage;
2223

2324
/**
@@ -77,10 +78,16 @@ public ObservableList<Routine> getFilteredRoutineList() {
7778
return model.getFilteredRoutineList();
7879
}
7980

81+
@Override
8082
public ObservableList<Lesson> getFilteredLessonList() {
8183
return model.getFilteredLessonList();
8284
}
8385

86+
@Override
87+
public ObservableList<Slot> getFilteredSlotList() {
88+
return model.getFilteredSlotList();
89+
}
90+
8491
@Override
8592
public Path getAddressBookFilePath() {
8693
return model.getAddressBookFilePath();

src/main/java/seedu/address/model/AddressBook.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ public void setLessons(List<Lesson> lessons) {
104104
this.lessons.setLessons(lessons);
105105
}
106106

107+
/**
108+
* Replaces the contents of slot list with {@code slots}.
109+
* {@code slots} must not contain duplicate slots or overlapping slots.
110+
*/
111+
public void setSlots(List<Slot> slots) {
112+
timetable.setSlots(slots);
113+
}
114+
107115
/**
108116
* Resets the existing data of this {@code AddressBook} with {@code newData}.
109117
*/
@@ -114,6 +122,7 @@ public void resetData(ReadOnlyAddressBook newData) {
114122
setExercises(newData.getExerciseList());
115123
setLessons(newData.getLessonList());
116124
setRoutines(newData.getRoutineList());
125+
setSlots(newData.getSlotList());
117126
}
118127

119128
//// person-level operations

src/main/java/seedu/address/model/person/Duration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
55

66
import java.time.LocalTime;
7+
import java.time.format.DateTimeFormatter;
78
import java.util.Objects;
89

910
public class Duration {
@@ -89,6 +90,7 @@ public int hashCode() {
8990

9091
@Override
9192
public String toString() {
92-
return startTime.toString() + " to " + endTime.toString();
93+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
94+
return startTime.format(formatter) + "-" + endTime.format(formatter);
9395
}
9496
}

src/main/java/seedu/address/model/person/Slot.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,6 @@ public int hashCode() {
100100

101101
@Override
102102
public String toString() {
103-
return activity.getName() + " on " + day.toString() + " from " + duration.toString();
103+
return activity.getName() + " on " + day.toString() + " " + duration.toString();
104104
}
105105
}

src/main/java/seedu/address/model/person/Timetable.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import static java.util.Objects.requireNonNull;
44

5+
import java.util.List;
6+
57
import javafx.collections.ObservableList;
68

79
public class Timetable {
@@ -40,6 +42,10 @@ public void deleteSlot(Slot slot) {
4042
slots.remove(slot);
4143
}
4244

45+
public void setSlots(List<Slot> slots) {
46+
this.slots.setSlots(slots);
47+
}
48+
4349
public ObservableList<Slot> getSlotList() {
4450
return slots.asUnmodifiableObservableList();
4551
}

src/main/java/seedu/address/model/person/UniqueSlotList.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.util.Objects.requireNonNull;
44

55
import java.util.Iterator;
6+
import java.util.List;
67

78
import javafx.collections.FXCollections;
89
import javafx.collections.ObservableList;
@@ -72,6 +73,56 @@ public void remove(Slot toRemove) {
7273
}
7374
}
7475

76+
public void setSlots(UniqueSlotList replacement) {
77+
requireNonNull(replacement);
78+
internalList.setAll(replacement.internalList);
79+
}
80+
81+
/**
82+
* Replaces the contents of this list with {@code slots}.
83+
* {@code slots} must not contain duplicate slots.
84+
*/
85+
public void setSlots(List<Slot> slots) {
86+
requireNonNull(slots);
87+
if (!areSlotsUnique(slots)) {
88+
throw new DuplicateSlotException();
89+
}
90+
91+
if (areSlotsOverlapping(slots)) {
92+
throw new SlotOverlapDurationException();
93+
}
94+
95+
internalList.setAll(slots);
96+
}
97+
98+
/**
99+
* Returns true if {@code slots} contains only unique slots.
100+
*/
101+
public boolean areSlotsUnique(List<Slot> slots) {
102+
for (int i = 0; i < slots.size() - 1; i++) {
103+
for (int j = i + 1; j < slots.size(); j++) {
104+
if (slots.get(i).isSameSlot(slots.get(j))) {
105+
return false;
106+
}
107+
}
108+
}
109+
return true;
110+
}
111+
112+
/**
113+
* Returns true if {@code slots} contains no overlapping slots.
114+
*/
115+
public boolean areSlotsOverlapping(List<Slot> slots) {
116+
for (int i = 0; i < slots.size() - 1; i++) {
117+
for (int j = i + 1; j < slots.size(); j++) {
118+
if (slots.get(i).hasOverlapDuration(slots.get(j))) {
119+
return true;
120+
}
121+
}
122+
}
123+
return false;
124+
}
125+
75126
/**
76127
* Returns the backing list as an unmodifiable {@code ObservableList}.
77128
*/

src/main/java/seedu/address/storage/JsonAdaptedRoutine.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public Routine toModelType() throws IllegalValueException {
6666
}
6767
final Name modelName = new Name(routineName);
6868

69-
final Set<Exercise> modelExercise = new HashSet<Exercise>(routineExercises);
69+
final Set<Exercise> modelExercise = new HashSet<>(routineExercises);
7070

7171
Routine modelRoutine = new Routine(modelName);
7272
for (Exercise exercise : modelExercise) {
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package seedu.address.storage;
2+
3+
import java.time.LocalTime;
4+
import java.util.ArrayList;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
9+
10+
import com.fasterxml.jackson.annotation.JsonCreator;
11+
import com.fasterxml.jackson.annotation.JsonProperty;
12+
13+
import seedu.address.commons.exceptions.IllegalValueException;
14+
import seedu.address.model.person.Activity;
15+
import seedu.address.model.person.Day;
16+
import seedu.address.model.person.Duration;
17+
import seedu.address.model.person.Exercise;
18+
import seedu.address.model.person.Lesson;
19+
import seedu.address.model.person.Name;
20+
import seedu.address.model.person.Routine;
21+
import seedu.address.model.person.Slot;
22+
import seedu.address.model.tag.Tag;
23+
24+
/**
25+
* Jackson-friendly version of {@link Slot}.
26+
*/
27+
public class JsonAdaptedSlot {
28+
public static final String TYPE_ROUTINE = "routine";
29+
public static final String TYPE_LESSON = "lesson";
30+
31+
public static final String MISSING_FIELD_MESSAGE_FORMAT = "Slot's %s field is missing!";
32+
33+
private final String activityName;
34+
private final String type;
35+
private final String day;
36+
private final String duration;
37+
private final List<JsonAdaptedTag> tagsForLesson = new ArrayList<>();
38+
private final List<JsonAdaptedExercise> exercisesForRoutine = new ArrayList<>();
39+
40+
/**
41+
* Constructs a {@code JsonAdaptedSlot} with the given slot details.
42+
*/
43+
@JsonCreator
44+
public JsonAdaptedSlot(@JsonProperty("activityName") String activityName,
45+
@JsonProperty("type") String type,
46+
@JsonProperty("day") String day,
47+
@JsonProperty("duration") String duration,
48+
@JsonProperty("tagsForLesson") List<JsonAdaptedTag> tags,
49+
@JsonProperty("exercisesForRoutine") List<JsonAdaptedExercise> exercises) {
50+
this.activityName = activityName;
51+
this.type = type;
52+
this.day = day;
53+
this.duration = duration;
54+
if (type.equals(TYPE_LESSON)) {
55+
if (tags != null) {
56+
tagsForLesson.addAll(tags);
57+
}
58+
} else {
59+
if (exercises != null) {
60+
exercisesForRoutine.addAll(exercises);
61+
}
62+
}
63+
}
64+
65+
/**
66+
* Converts a given {@code Slot} into this class for Jackson use.
67+
*/
68+
public JsonAdaptedSlot(Slot source) {
69+
activityName = source.getActivity().getName().fullName;
70+
type = source.getActivity() instanceof Routine
71+
? TYPE_ROUTINE
72+
: TYPE_LESSON;
73+
day = source.getDay().toString();
74+
duration = source.getDuration().toString();
75+
if (type.equals(TYPE_LESSON)) {
76+
Lesson lesson = (Lesson) source.getActivity();
77+
tagsForLesson.addAll(lesson.getTags()
78+
.stream()
79+
.map(JsonAdaptedTag::new)
80+
.collect(Collectors.toList()));
81+
} else {
82+
Routine routine = (Routine) source.getActivity();
83+
exercisesForRoutine.addAll(routine.getExercises()
84+
.stream()
85+
.map(JsonAdaptedExercise::new)
86+
.collect(Collectors.toList()));
87+
}
88+
}
89+
90+
/**
91+
* Converts this Jackson-friendly adapted slot object into the model's {@code Slot} object.
92+
*
93+
* @throws IllegalValueException if there were any data constraints violated in the adapted exercise.
94+
*/
95+
public Slot toModelType() throws IllegalValueException {
96+
97+
if (activityName == null) {
98+
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
99+
}
100+
if (!Name.isValidName(activityName)) {
101+
throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
102+
}
103+
if (Day.isUnknownDay(Day.getDayEnum(day))) {
104+
throw new IllegalValueException(Day.MESSAGE_CONSTRAINTS);
105+
}
106+
if (!Duration.isValidDuration(duration)) {
107+
throw new IllegalValueException(Duration.MESSAGE_CONSTRAINTS_FORMAT);
108+
}
109+
110+
final Name modelName = new Name(activityName);
111+
112+
final Activity modelActivity;
113+
if (type.equals(TYPE_LESSON)) {
114+
final List<Tag> lessonTags = new ArrayList<>();
115+
for (JsonAdaptedTag tag : tagsForLesson) {
116+
lessonTags.add(tag.toModelType());
117+
}
118+
final Set<Tag> modelTags = new HashSet<>(lessonTags);
119+
modelActivity = new Lesson(modelName, modelTags);
120+
} else {
121+
final List<Exercise> routineExercises = new ArrayList<>();
122+
for (JsonAdaptedExercise exercise : exercisesForRoutine) {
123+
routineExercises.add(exercise.toModelType());
124+
}
125+
final Set<Exercise> modelExercise = new HashSet<>(routineExercises);
126+
Routine modelRoutine = new Routine(modelName);
127+
for (Exercise exercise : modelExercise) {
128+
modelRoutine.addExercise(exercise);
129+
}
130+
modelActivity = modelRoutine;
131+
}
132+
133+
final Day modelDay = Day.getDayEnum(day);
134+
135+
final Duration modelDuration = parseDuration(duration);
136+
137+
return new Slot(modelActivity, modelDay, modelDuration);
138+
}
139+
140+
private Duration parseDuration(String toParse) throws IllegalValueException {
141+
String[] timeSplit = toParse.split("-");
142+
143+
int startHour = Integer.parseInt(timeSplit[0].substring(0, 2));
144+
int startMinute = Integer.parseInt(timeSplit[0].substring(2, 4));
145+
LocalTime startTime = LocalTime.of(startHour, startMinute);
146+
147+
int endHour = Integer.parseInt(timeSplit[1].substring(0, 2));
148+
int endMinute = Integer.parseInt(timeSplit[1].substring(2, 4));
149+
LocalTime endTime = LocalTime.of(endHour, endMinute);
150+
151+
if (!Duration.isValidDuration(startTime, endTime)) {
152+
throw new IllegalValueException(Duration.MESSAGE_CONSTRAINTS_ORDER);
153+
}
154+
155+
return new Duration(startTime, endTime);
156+
}
157+
}

0 commit comments

Comments
 (0)