diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..fc38d900a67 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -56,7 +56,7 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); + AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getTrackerFilePath()); storage = new StorageManager(addressBookStorage, userPrefsStorage); initLogging(config); diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..5dab0ffa07b 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,15 +5,20 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.module.Module; import seedu.address.model.person.Person; /** * The API of the Model component. */ public interface Model { + // TODO: Remove this /** {@code Predicate} that always evaluate to true */ Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_MODULES = unused -> true; + /** * Replaces user prefs data with the data in {@code userPrefs}. */ @@ -34,41 +39,91 @@ public interface Model { */ void setGuiSettings(GuiSettings guiSettings); + // TODO: Replace this /** * Returns the user prefs' address book file path. */ Path getAddressBookFilePath(); + // TODO: Replace this /** * Sets the user prefs' address book file path. */ void setAddressBookFilePath(Path addressBookFilePath); + /** + * Replaces tracker data with the data in {@code tracker}. + */ + void setTracker(ReadOnlyTracker tracker); + + /** + * Returns the Tracker. + */ + ReadOnlyTracker getTracker(); + + /** + * Returns true if a module with the same code as {@code module} exists in the tracker. + */ + boolean hasModule(Module module); + + /** + * Deletes the given module. + * The module must exist in the tracker. + */ + void deleteModule(Module target); + + /** + * Adds the given module. + */ + void addModule(Module module); + + /** + * Replaces the given module {@code target} with {@code editedModule}. + * {@code target} must exist in the tracker. + * The module code of {@code editedPerson} must not be the same as another existing module in the tracker. + */ + void setModule(Module target, Module editedModule); + + /** Returns an unmodifiable view of the filtered module list */ + ObservableList getFilteredModuleList(); + + /** + * Updates the filter of the filtered module list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredModuleList(Predicate predicate); + + // TODO: Remove this /** * Replaces address book data with the data in {@code addressBook}. */ void setAddressBook(ReadOnlyAddressBook addressBook); + // TODO: Remove this /** Returns the AddressBook */ ReadOnlyAddressBook getAddressBook(); + // TODO: Remove this /** * Returns true if a person with the same identity as {@code person} exists in the address book. */ boolean hasPerson(Person person); + // TODO: Remove this /** * Deletes the given person. * The person must exist in the address book. */ void deletePerson(Person target); + // TODO: Remove this /** * Adds the given person. * {@code person} must not already exist in the address book. */ void addPerson(Person person); + // TODO: Remove this /** * Replaces the given person {@code target} with {@code editedPerson}. * {@code target} must exist in the address book. @@ -76,9 +131,11 @@ public interface Model { */ void setPerson(Person target, Person editedPerson); + // TODO: Remove this /** Returns an unmodifiable view of the filtered person list */ ObservableList getFilteredPersonList(); + // TODO: Remove this /** * Updates the filter of the filtered person list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 86c1df298d7..90a4a9763bb 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,6 +11,7 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.model.module.Module; import seedu.address.model.person.Person; /** @@ -19,10 +20,12 @@ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final Tracker tracker; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final FilteredList filteredModules; + private final AddressBook addressBook; // TODO: Remove this + private final FilteredList filteredPersons; // TODO: Remove this /** * Initializes a ModelManager with the given addressBook and userPrefs. */ @@ -31,8 +34,10 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + this.tracker = new Tracker(); //TODO: assign this from constructor arguments this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); + filteredModules = new FilteredList<>(this.tracker.getModuleList()); filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); } @@ -66,13 +71,61 @@ public void setGuiSettings(GuiSettings guiSettings) { @Override public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); + return userPrefs.getTrackerFilePath(); } @Override public void setAddressBookFilePath(Path addressBookFilePath) { requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); + userPrefs.setTrackerFilePath(addressBookFilePath); + } + + //=========== Tracker ==================================================================================== + + @Override + public void setTracker(ReadOnlyTracker tracker) { + this.tracker.resetData(tracker); + } + + @Override + public ReadOnlyTracker getTracker() { + return tracker; + } + + @Override + public boolean hasModule(Module module) { + return tracker.hasModule(module); + } + + @Override + public void deleteModule(Module target) { + tracker.removeModule(target); + } + + @Override + public void addModule(Module module) { + tracker.addModule(module); + updateFilteredModuleList(PREDICATE_SHOW_ALL_MODULES); + } + + @Override + public void setModule(Module target, Module editedModule) { + requireAllNonNull(target, editedModule); + + tracker.setModule(target, editedModule); + } + + //=========== Filtered Module List Accessors ============================================================= + + @Override + public ObservableList getFilteredModuleList() { + return filteredModules; + } + + @Override + public void updateFilteredModuleList(Predicate predicate) { + requireNonNull(predicate); + filteredModules.setPredicate(predicate); } //=========== AddressBook ================================================================================ @@ -144,7 +197,9 @@ public boolean equals(Object obj) { ModelManager other = (ModelManager) obj; return addressBook.equals(other.addressBook) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredPersons.equals(other.filteredPersons) + && tracker.equals(other.tracker) + && filteredModules.equals(other.filteredModules); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyTracker.java b/src/main/java/seedu/address/model/ReadOnlyTracker.java new file mode 100644 index 00000000000..8f559f7396e --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyTracker.java @@ -0,0 +1,15 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; + +/** + * Unmodifiable view of a tracker + */ +public interface ReadOnlyTracker { + + /** + * Returns an unmodifiable view of the module list. + * This list will not contain any duplicate modules. + */ + ObservableList getModuleList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..ad714955e32 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getTrackerFilePath(); } diff --git a/src/main/java/seedu/address/model/Tracker.java b/src/main/java/seedu/address/model/Tracker.java new file mode 100644 index 00000000000..ad8c4ca3751 --- /dev/null +++ b/src/main/java/seedu/address/model/Tracker.java @@ -0,0 +1,119 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.module.Module; +import seedu.address.model.module.UniqueModuleList; + +/** + * Wraps all data at the tracker level + * Duplicate modules are not allowed (by .isSameModule comparison) + */ +public class Tracker implements ReadOnlyTracker { + private final UniqueModuleList modules; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + modules = new UniqueModuleList(); + } + + public Tracker() {} + + /** + * Creates a Tracker using the Modules in the {@code toBeCopied} + */ + public Tracker(ReadOnlyTracker toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the module list with {@code modules}. + * {@code modules} must not contain duplicate modules. + */ + public void setModules(List modules) { + this.modules.setModules(modules); + } + + /** + * Resets the existing data of this {@code Tracker} with {@code newData}. + */ + public void resetData(ReadOnlyTracker newData) { + requireNonNull(newData); + + setModules(newData.getModuleList()); + } + + //// module-level operations + + /** + * Returns true if a module with the same code as {@code module} exists in the tracker. + */ + public boolean hasModule(Module module) { + requireNonNull(module); + return modules.contains(module); + } + + /** + * Adds a module to the tracker. + * The module must not already exist in the tracker. + */ + public void addModule(Module module) { + modules.add(module); + } + + /** + * Replaces the given module {@code target} in the list with {@code editedModule}. + * {@code target} must exist in the tracker. + * The module of {@code editedModule} must not be the same as another existing module in the tracker. + */ + public void setModule(Module target, Module editedModule) { + requireNonNull(editedModule); + + modules.setModule(target, editedModule); + } + + /** + * Removes {@code key} from this {@code Tracker}. + * {@code key} must exist in the tracker. + */ + public void removeModule(Module key) { + modules.remove(key); + } + + //// util methods + + @Override + public String toString() { + return modules.asUnmodifiableObservableList().size() + " modules"; + // TODO: refine later + } + + @Override + public ObservableList getModuleList() { + return modules.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Tracker // instanceof handles nulls + && modules.equals(((Tracker) other).modules)); + } + + @Override + public int hashCode() { + return modules.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UniqueDataList.java b/src/main/java/seedu/address/model/UniqueDataList.java new file mode 100644 index 00000000000..bc4ad8a97da --- /dev/null +++ b/src/main/java/seedu/address/model/UniqueDataList.java @@ -0,0 +1,163 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.BiPredicate; +import java.util.function.Supplier; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +/** + * A list of data that enforces uniqueness between its elements and does not allow nulls. + * A data is considered unique by comparing using {@code isSameChecker.test(T, T)}. As such, adding and updating of + * data uses {@code isSameChecker.test(T, T)} for equality so as to ensure that the data being added or updated is + * unique in the UniqueDataList. However, the removal of a data uses {@code T#equals(Object)} so as to ensure that + * the data with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + */ +public class UniqueDataList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + private final BiPredicate isSameChecker; + private final Supplier duplicateExceptionCreator; + private final Supplier notFoundExceptionCreator; + + /** + * Constructs a {@code UniqueDataList}. + * + * @param isSameChecker Use to check if 2 data are the same. + * @param duplicateExceptionCreator Creates any exception thrown due to duplicate data. + * @param notFoundExceptionCreator Creates any exception thrown due to data not found. + */ + public UniqueDataList(BiPredicate isSameChecker, + Supplier duplicateExceptionCreator, + Supplier notFoundExceptionCreator) { + + this.isSameChecker = isSameChecker; + this.duplicateExceptionCreator = duplicateExceptionCreator; + this.notFoundExceptionCreator = notFoundExceptionCreator; + } + + /** + * Returns true if the list contains an equivalent data as the given argument. + */ + public boolean contains(T toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch((data) -> isSameChecker.test(toCheck, data)); + } + + /** + * Adds a data to the list. + * The data must not already exist in the list. + */ + public void add(T toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw duplicateExceptionCreator.get(); + } + internalList.add(toAdd); + } + + /** + * Replaces the data {@code target} in the list with {@code editedData}. + * {@code target} must exist in the list. + * {@code editedData} must not be the same as another existing data in the list. + */ + public void setData(T target, T editedData) { + requireAllNonNull(target, editedData); + + int index = internalList.indexOf(target); + if (index == -1) { + throw notFoundExceptionCreator.get(); + } + + if (!isSameChecker.test(target, editedData) && contains(editedData)) { + throw duplicateExceptionCreator.get(); + } + + internalList.set(index, editedData); + } + + /** + * Removes the equivalent data from the list. + * The data must exist in the list. + */ + public void remove(T toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw notFoundExceptionCreator.get(); + } + } + + public void setAllData(UniqueDataList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code data}. + * {@code data} must not contain duplicate data. + */ + public void setAllData(List data) { + requireAllNonNull(data); + if (!dataAreUnique(data)) { + throw duplicateExceptionCreator.get(); + } + + internalList.setAll(data); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + /** + * Returns the backing list as an unmodifiable {@code List}. + */ + public List asUnmodifiableList() { + return Collections.unmodifiableList(internalList); + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueDataList // instanceof handles nulls + && internalList.equals(((UniqueDataList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code data} contains only unique data. + */ + private boolean dataAreUnique(List data) { + for (int i = 0; i < data.size() - 1; i++) { + for (int j = i + 1; j < data.size(); j++) { + if (isSameChecker.test(data.get(i), data.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..336f7f26b68 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path trackerFilePath = Paths.get("data" , "letracker.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setTrackerFilePath(newUserPrefs.getTrackerFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getTrackerFilePath() { + return trackerFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setTrackerFilePath(Path trackerFilePath) { + requireNonNull(trackerFilePath); + this.trackerFilePath = trackerFilePath; } @Override @@ -68,19 +68,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && trackerFilePath.equals(o.trackerFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, trackerFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + trackerFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/lecture/Lecture.java b/src/main/java/seedu/address/model/lecture/Lecture.java new file mode 100644 index 00000000000..841f74849e4 --- /dev/null +++ b/src/main/java/seedu/address/model/lecture/Lecture.java @@ -0,0 +1,118 @@ +package seedu.address.model.lecture; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.tag.Tag; +import seedu.address.model.video.UniqueVideoList; +import seedu.address.model.video.Video; + +/** + * Represents a lecture of a module that is in the tracker.. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Lecture { + + private final LectureName name; + + private final Set tags = new HashSet<>(); + + private final UniqueVideoList videos = new UniqueVideoList(); + + /** + * Constructs a {@code Lecture}. + * + * @param name The name of the lecture. + * @param tags The tags applied to the module. + */ + public Lecture(LectureName name, Set tags, List