diff --git a/README.md b/README.md index 13f5c77403f..8aea3ad4abf 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2122S1-CS2103T-W12-1/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2122S1-CS2103T-W12-1/tp/actions/workflows/gradle.yml) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-W12-1/tp/branch/master/graph/badge.svg?token=W77BXICS47)](https://codecov.io/gh/AY2122S1-CS2103T-W12-1/tp) + +# SportsPA + +SportsPA is a desktop application used to manage membership and training sessions of NUS sports CCAs. ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +SportsPA is +* text-based +* Easy to use +* Simple UI +* Fast to use + +Use SportsPA to +* track member contact information, attendance and availability +* track booked facilities +* allocate members to different locations for training sessions + +View detailed documentation of SportsPA [here](https://ay2122s1-cs2103t-w12-1.github.io/tp/) + +This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/build.gradle b/build.gradle index be2d2905dde..0f60ec82842 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'SportsPA.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..7af7494bf00 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,42 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Felix Ong Wei Cong - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] - -* Role: Project Advisor - -### Jane Doe - - - -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/felix-ong)] +[[portfolio](team/felix-ong.md)] * Role: Team Lead * Responsibilities: UI -### Johnny Doe +### Ong Xing Wei - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/Moley456)] +[[portfolio](team/moley456.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Data + Storage -### Jean Doe +### Seow Xiu Wen - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/YoYoCiti)] +[[portfolio](team/yoyociti.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Dev Ops + Documentation -### James Doe +### Teo Sin Yee - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/tsinyee)] +[[portfolio](team/tsinyee.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Model diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..e6e79c922bc 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,15 +1,64 @@ --- layout: page title: Developer Guide +nav-text: Developer Guide --- + +

+
+

+ + +Welcome to the SportsPA Developer Guide! + + +-------------------------------------------------------------------------------------------------------------------- + + + +Table of Contents + * Table of Contents {:toc} +
+ -------------------------------------------------------------------------------------------------------------------- +## **Introduction** +SportsPA is a desktop application for NUS sports *CCA* Leaders to manage membership and +training sessions, optimized for use via a *Command Line Interface* (CLI), while still preserving the benefits of a *Graphical User Interface* (GUI). + +SportsPA is highly optimized for *fast typists* and can be fully operated through keyboard commands. + +This developer guide is intended to be a one-stop source for anyone interested in extending and modifying SportsPA. +For certain terms that are unique to SportsPA, a [glossary](#glossary) has been provided for readers. + +-------------------------------------------------------------------------------------------------------------------- +## **Using this Developer Guide** + +You can click on the links in the [Table of Contents](#table-of-contents) to quickly navigate to your desired location in +this Developer Guide. A link to return to the [Table of Contents](#table-of-contents) is also provided at the end of every section. +
+The table below summarizes the meaning of the icons and text styles used throughout this Developer Guide. +
+ +Syntax | Description +----------------- | ------------------ +**bold** | Highlights important information such as components of SportsPA or constraints of command parameters +*italics* | Terms to be defined in the [glossary](#glossary) +`Codeblock` | Represents distinct classes, and their methods +keyboard | Represents keyboard actions by the user +[link](#table-of-contents) | Represents links that can be clicked on to navigate to a relevant section of the User Guide or a different website +**:information_source: Notes:** | Represents important information to note +**:bulb: Tip:**| Represents useful tips that we would like to share + +[Back to Table of Contents](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------- ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* Based on AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org/). -------------------------------------------------------------------------------------------------------------------- @@ -17,13 +66,23 @@ title: Developer Guide Refer to the guide [_Setting up and getting started_](SettingUp.md). +
+ +**:information_source: Note:** Readers are advised to [download](https://github.com/AY2122S1-CS2103T-W12-1/tp/releases) SportsPA's latest release to test the application. +
+ +
+ -------------------------------------------------------------------------------------------------------------------- ## **Design**
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in +the [diagrams](https://github.com/AY2122S1-CS2103T-W12-1/tp/tree/master/docs/diagrams) folder. Refer to the [_PlantUML +Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit +diagrams.
### Architecture @@ -36,7 +95,11 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes +called [`Main`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/Main.java) +and [`MainApp`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/MainApp.java). It +is responsible for, + * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -49,60 +112,78 @@ The rest of the App consists of four components. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. - -**How the architecture components interact with each other** - -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. - - - Each of the four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. -* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding + API `interface` mentioned in the previous point. -For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. +For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using +the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component +through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the +implementation of a component), as illustrated in the (partial) class diagram below. +**How the architecture components interact with each other** + +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues +the command deletem 1. + + + The sections below give more details of each component. +
+ ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified +in [`Ui.java`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `MemberListPanel` +, `FacilityListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures +the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that +are in the `src/main/resources/view` folder. For example, the layout of +the [`MainWindow`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) +is specified +in [`MainWindow.fxml`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Member` and `Facility` objects residing in the `Model`. ### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: +
+ How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). + +1. When `Logic` is called upon to execute a command, it uses the `SportsPaParser` class to parse the user command. +1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddMemberCommand`) + which is executed by the `LogicManager`. +1. The command can communicate with the `Model` when it is executed (e.g. to add a member). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("deletem 1")` API +call. ![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +
:information_source: **Note:** The lifeline for `DeleteMemberCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: @@ -110,43 +191,58 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. + +* `SportsPaParser` depends on some classes in the `Model` component (as it is responsible for identifying and parsing aliases present in a user command). +* When called upon to parse a user command, the `SportsPaParser` class creates an `XYZCommandParser` (`XYZ` is a + placeholder for the specific command name e.g., `AddMemberCommandParser`) which uses the other classes shown above to + parse the user command and create a `XYZCommand` object (e.g., `AddMemberCommand`) which the `SportsPaParser` + returns back as a `Command` object. +* All `XYZCommandParser` classes (e.g., `AddMemberCommandParser`, `DeleteMemberCommandParser`, ...) inherit from the `Parser` + interface so that they can be treated similarly where possible e.g, during testing. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) - +**API** : [`Model.java`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/model/Model.java) + + The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. -* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. -* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) +* stores the SportsPA data i.e., all `Member` and `Facility` objects (which are contained in a `UniqueMemberList` +and a `UniqueFacilityList` object respectively). +* stores the currently 'selected' `Member` and `Facility` objects (e.g., results of a search query) as a separate _filtered_ list which + is exposed to outsiders as unmodifiable `ObservableList` and `ObservableList` respectively which can be 'observed' e.g. the UI can be bound to + this list so that the UI automatically updates when the data in the list change. +* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as + a `ReadOnlyUserPref` objects. +* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they + should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. Note that `Facility` classes are omitted in this diagram. It has a `Tag` list in the `SportsPA`, which `Member` references. This allows `SportsPa` to only require one `Tag` object per unique tag, instead of each `Member` needing their own `Tag` objects.
- +
- ### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S1-CS2103T-W12-1/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). -* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) + +* can save both SportsPA data (data on members and facilities) and user preference data in *json* format, and read them back into corresponding + objects. +* inherits from both `SportsPaStorage` and `UserPrefStorage`, which means it can be treated as either one (if only + the functionality of only one is needed). +* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects + that belong to the `Model`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.address.commons` package. -------------------------------------------------------------------------------------------------------------------- @@ -154,90 +250,352 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +
:information_source: **Note:** For all sequence diagrams where object deletion is shown, the lifeline for the object should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. + +
-#### Proposed Implementation +### Alias feature +The alias feature creates a shortcut name for any command in SportsPA. The shortcut name must not be an existing command word in SportsPA. +#### Implementation -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +The *alias* mechanism is facilitated by `SportsPaParser`. Aliases are stored in `AliasMap`, which keeps the mappings +between `Shortcut` and `CommandWord`, and is stored in `UserPref`. The association between `Shortcut` and `CommandWord` +is represented as `Alias`. The class diagram for `AliasMap` is shown below. -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +![AliasClassDiagram](images/AliasClassDiagram.png) -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +`AliasMap` implements the following operations: -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +* `AliasMap#add(Alias)` — Adds an alias to the mapping. +* `AliasMap#remove(Shortcut)` — Removes an alias from the mapping. +* `AliasMap#convertAliasIfPresent(String)` — Replaces the input string with the command if it is an alias. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +The first two operations are exposed in the `Model` interface as `Model#addAlias(Alias)` +and `Model#removeAlias(Shortcut)` respectively. The last operation is utilised by `SportsPaParser` to parse aliases used +in a user command. The relevant operation to retrieve the `AliasMap` is exposed in the `Model` interface as `Model#getAliases()`. -![UndoRedoState0](images/UndoRedoState0.png) +Given below is an example usage scenario and how the aliases mechanism behaves. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +Step 1. The user launches the application for the first time. `UserPrefs` is initialised and `AliasMap` is created with +empty mappings. -![UndoRedoState1](images/UndoRedoState1.png) +![AliasState0](images/AliasState0.png) -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 2. The user executes the alias s/l cw/listf command to create a shortcut l for the listf command. The alias +command calls `Model#addAlias(Alias)`, causing a mapping between l and listf to be stored in `AliasMap`. -![UndoRedoState2](images/UndoRedoState2.png) +![AliasState1](images/AliasState1.png) -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +Step 3. The user now wants to use the shortcut l for a different command, listm, instead. The user +executes alias s/l cw/listm. `Model#addAlias(Alias)` is called again and the mapping from l to listf is replaced +with l to listm in `AliasMap`. -
+![AliasState2](images/AliasState2.png) -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Step 4. The user enters l, which the system understands as listm and executes the listm command, displaying all +members in the member list. -![UndoRedoState3](images/UndoRedoState3.png) +The following sequence diagram shows how the system understands aliases: -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +![AliasSequenceDiagram](images/AliasSequenceDiagram.png) -
+Step 5. The user then realises that the shortcut l was not to their liking and deletes the alias by +executing unalias l. The unalias command calls `Model#removeAlias(Shortcut)` and removes the mapping from `AliasMap` +. -The following sequence diagram shows how the undo operation works: +![AliasState3](images/AliasState3.png) -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +Step 6. The user finally decides to use the shortcut lm for listm and executes alias s/lm cw/listm. The user +closes the application and the alias defined are saved into `UserPrefStorage`, available for use at the next launch. -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +The following activity diagram summarizes what happens when a user enters and executes a command: -
+ -The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +#### Design considerations: -
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +**Aspect: What the user can create shortcuts for** -
+* **Alternative 1 (current choice):** Valid commands only. + * Pros: Easy to implement in parser. + * Cons: Less flexibility for user. + +* **Alternative 2:** Any defined text. + * Pros: Very flexible, no validation required. + * Cons: Difficult to parse user input as would need to scan exhaustively for aliases. + +In modern CLI applications, an alias is mainly used for abbreviating system commands or adding default arguments to +regularly used commands. Considering the target use of SportsPA and time constraints, since command arguments are not +likely to be repeated, we decided that it was sufficient to allow users to create shortcuts for commands only. + +### Split members to facilities feature +The split feature splits members into the facilities based on its capacity and members' availabilities. The split command only accepts **numbers 1-7** as a preamble e.g., split 6 is valid and splits members on Saturday. +#### Implementation + +The split mechanism is facilitated by `ModelManager` and `SportsPa`.

`ModelManager` stores a list of +filtered members as `filteredMembers`. Each `Member` in the list has an `Availability`, which is implemented internally as a `List`. +
+`SportsPa` stores a list of all facilities in `UniqueFacilityList` as `facilities`. Each `Facility` in the list has an `AllocationMap`, which is implemented internally as an `EnumMap>`. This `EnumMap` is initialized +with 7 key-value pairs, of which the keys are all the enums of the +`java.time.DayOfWeek` (`{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}`) and the values are all initialized +as an empty `ArrayList` to hold the members allocated. This is based on the assumption that facilities are available on every day of the week. +
+When the `split` command is executed with a given `DAY` parameter, all members available on that `DAY` are filtered and the `ArrayList` of all facilities for that `DAY` is cleared. +The available members are then added to the `List` of the corresponding `DayOfWeek` in the `EnumMap` of the facilities using a greedy algorithm. +
+i.e. The filtered members list and facility list are iterated and each `Member` is allocated to the first `Facility` which is not at max capacity. After +a `Facility` is at max capacity, any remaining `Member`s are allocated to the next available `Facility` and so on. + +`ModelManager` implements the following operation: +* `ModelManager#split(Predicate, int)` — Filters the list of all members according to the given `predicate`. + +`SportsPa` implements the following operation: +* `SportsPa#split(FilteredList, int)` — Splits the members in the given filtered member list into facilities on the given day. -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Additionally,
+`UniqueFacilityList` implements the following operation: +* `UniqueFacilityList#allocateMembersToFacilitiesOnDay(FilteredList, int)` — Clears the `AllocationMap` of each `Facility` +and allocates the members in the given filtered member list to facilities greedily. +* +`Facility` implements the following operation: +* `Facility#addMemberToFacilityOnDay(Member, DayOfWeek)` — Adds the given member to `AllocationMap` on the given day. -![UndoRedoState4](images/UndoRedoState4.png) +Given below is an example usage scenario and how the split feature behaves at each step. -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +Step 1. The user launches the application for the first time. The user then adds 5 members into an empty SportsPA +by executing the addm command 5 times with the parameter d/1 (all required parameters are provided as well but not specified here). +Each `Member` in the `filteredMembers` list will have an availability of Monday. +The user then adds 1 facility into SportsPA by executing the addf command with the parameter c/5 +(all required parameters are provided as well but not specified here). The `Facility` in the `facilities` list will +have a capacity of 5 and an `AllocationMap` with all the values initialized as an empty `ArrayList`. -![UndoRedoState5](images/UndoRedoState5.png) +Step2. The user executes split 1 command to split the 5 members in the filtered list to facilities on Monday. The split command +creates a `MemberAvailableOnDayPredicate` with the given day and passes it and the given day to `ModelManager#split(Predicate, int)`. +`ModelManager` then creates a filtered list of members who are available on Monday. It then calls `SportsPa#split(FilteredList, int)`, passing to it +the filtered list and the given day. `SportsPA` then iterates through the 5 members in the filtered member list and the 1 facility in its `UniqueFacilityList`, calling +`Facility#addMemberToFacilityOnDay(Member, DayOfWeek)`. This adds the 5 members to the `ArrayList` of the `AllocationMap` of the `Facility` for Monday. -The following activity diagram summarizes what happens when a user executes a new command: +The following sequence diagram shows how the split mechanism works. - +![SplitSequenceDiagram](images/SplitSequenceDiagram.png) + +The following activity diagram summarizes what happens when a user executes the `split` command: + + + +#### Design considerations: + +**Aspect: Format of values in `Availability`** + +* **Alternative 1 (current choice):** Store values as `java.time.DayOfWeek`. + * Pros: Easy to implement in parser, increases user-friendliness by allowing users to just type in numbers to + represent days of a week instead of the names. Numbers can then be easily converted into `DayOfWeek` and formatted + to get consistent display name formats. Easily sorted in the natural order of the days of a week, ensuring + uniformity when displayed and making it easier to read. + * Cons: May not be intuitive to some users that 1 represents Monday and 7 represents Sunday. + +* **Alternative 2:** Store values as `String` + * Pros: Intuitive for users to type in the names of the days which can be stored directly after parsing. + * Cons: Difficult to parse user input as a complicated regular expression is needed to ensure names of days are given + in the correct format. Less user-friendly due to need to type out the names of the days and more difficult to sort. + +**Aspect: Algorithm used to determine allocation** +* **Alternative 1 (current choice):** Greedy algorithm. + * Pros: Easy to implement and test. Intuitive and produces results similar to manual allocation. + * Cons: Can only produce 1 allocation mapping for a set of Member and facilities with the same availabilities and capacities, which may + not be ideal. + +* **Alternative 2:** Other algorithms. + * Pros: Possible increase in performance and able to produce multiple different mappings. + * Cons: Harder to implement and test. May require the use of supporting data structures which adds on to the complexity. + +### Mark/unmark attendance feature +The mark attendance feature marks the attendance of the members at the specified index/indices of the member list as present.
+The unmark attendance feature unmarks the attendance of the members at the specific index/indices of the member list to be absent.
+ +#### Implementation + +The mark/unmark attendance mechanism is facilitated by `ModelManager`. The `ModelManager` stores a list of filtered members +as `filteredMembers`. Each `Member` in the list internally stores `totalAttendance` and `todayAttendance` +which will be updated accordingly when the attendance of that `Member` is marked or unmarked. + +`ModelManager` implements the following operations: +* `ModelManager#markMembersAttendance(List)` — Marks attendance of members at the specified list of index. +* `ModelManager#unmarkMembersAttendance(List)` — Unmarks attendance of members at the specified list of index +as absent. +* `ModelManager#markOneMemberAttendance(Member)` — Marks attendance of specified member. +* `ModelManager#unmarkMembersAttendance(Member)` — Unmarks attendance of specified member. +* `ModelManager#isWithinListIndex(List)` — Checks if given indices are valid. + +Additionally, `Member` implements the following operations: +* `Member#setPresent()` — Sets `todayAttendance` as present and increments `totalAttendance` +* `Member#setNotPresent()` — Sets `todayAttendance` as not present and decrements `totalAttendance` + +Given below is an example usage scenario and how the mark/unmark attendance feature behaves at each step. + +Step 1. The user launches the application for the first time. The user then adds 2 members into an empty SportsPA +by executing the `addm` command. Each `Member` in the `filteredMembers` list will be initialized with their initial +`todayAttendance` and `totalAttendance`. + +![MarkObjectDiagram](images/MarkObjectDiagram_InitialState.png) + + +Step 2. The user executes mark 1 2 command to mark the members at index 1 and 2 in the filtered list as present. The mark command +first checks if the given indices 1 and 2 are within the displayed list of members via the `ModelManager#isWithinListIndex(List)`. Then +if all indices are valid,`ModelManager#markMembersAttendance(List)` is then called. This then calls `ModelManager#markOneMemberAttendance(Member)` to increment `todayAttendance` +and `totalAttendance` of the `Member` at the 1st and 2nd index in the list by calling `Member#setPresent()` for each `Member`. The newly edited`Member`s with the updated attendance are now referenced by `ModelManager`. + +![MarkObjectDiagramModified](images/MarkObjectDiagramModified_FinalState.png) + +The following sequence diagram shows how the mark attendance operation works. + +![MarkSequenceDiagram](images/MarkSequenceDiagram.png) + +The unmark command does the opposite — it calls the `ModelManager#unmarkMembersAttendance(List)`, which then +calls the `ModelManager#unmarkMembersAttendance(Member)` which decrements the `totalAttendance` and `todayAttendance` of the `Member` +to be unmarked via the `Member#setNotPresent()` and `ModelManager` references the newly modified `Member`s. + +The following activity diagram shows what happens when a user executes the `mark` command. + + #### Design considerations: -**Aspect: How undo & redo executes:** +**Aspect: How mark & unmark executes** + +* **Alternative 1 (current choice):** Uses index to mark attendance. + * Pros: Easy to implement (e.g there will be no two members with the same index in list, so there will be no + ambiguity) + * Cons: May require additional step of finding members' index using findm command then marking attendance. + +* **Alternative 2:** Uses names of members to mark attendance. + * Pros: Requires one less step of finding members. + * Cons: There may be two members with same name, so when marking using names, it might result in ambiguity of whose + attendance to mark. + +### Find member feature +The find member feature finds and displays all members that match the parameters specified. **At least one valid parameter** must be supplied. +#### Implementation +The find member mechanism is facilitated by `FindMemberCommandParser`. +`FindMemberCommandParser` implements the following operations:
+ +* `FindMemberCommandParser#generatePredicate` — Generates the final predicate to be used for FindMemberCommand. +* `FindMemberCommandParser#generateNamePredicate` — Generates the unique name predicate. +* `FindMemberCommandParser#generatePhonePredicate` — Generates the unique phone predicate. +* `FindMemberCommandParser#generateTagPredicate` — Generates the unique tag predicate. +* `FindMemberCommandParser#generateAvailabilityPredicate` — Generates the unique availability predicate. +* `FindMemberCommandParser#generateTodayAttendancePredicate` — Generates the unique today attendance predicate. +* `FindMemberCommandParser#generateTotalAttendancePredicate` — Generates the unique total attendance predicate.
+ +The last six operations are facilitated by each attribute's unique `Predicate` class, the predicates generated are then chained together in `FindMemberCommandParser#generatePredicate` using the `Predicate#and` method.
+ +The final `Predicate` to filter the member list with is stored in `MemberMatchesKeywordsPredicate`, which is subsequently passed to the `FindMemberCommand` class to be executed.
+ +Lastly, the filtered member list is displayed through `Model#updateFilteredMemberList(Predicate)` + +Given below is an example usage scenario and how the find member mechanism behaves. + +Step 1. The user executes the findm t/exco command to find all members with the tag exco
+Step 2. `LogicManager` calls `SportsPaParser#parseCommand` and creates a new `FindMemberCommandParser`.
+Step 3. `FindMemberCommandParser#parse` is called to parse the argument t/exco.
+Step 4. Since t/exco is a valid argument,`FindMemberCommandParser#generatePredicate` is called.
+Step 5. The system recognises the t/ prefix and calls on the `FindMemberCommandParser#generateTagPredicate` to generate a unique tag predicate from exco.
+Step 6. The unique tag predicate is returned in the `FindMemberCommandParser#generatePredicate` method and then chained together via the `Predicate#and` method.
+Step 7. A new `MemberMatchesKeywordPredicate` object is created to store the final predicate.
+Step 8. The `MemberMatchesKeywordPredicate` object is passed to `FindMemberCommand`.
+Step 9. `FindMemberCommand` is then executed through `FindCommand#execute`.
+Step 10. `FindMemberCommand` will update the member list using the `Model#updateFilteredMemberList` method.
+Step 11. Lastly, a new `CommandResult` is returned to the `LogicManager`. + +The following sequence diagram shows how the find member operation works: + + + +The following activity diagram summarizes what happens when a user enters and executes a find member command: + + + +#### Design considerations +**Aspect: Implementation of the find member command** + +* **Alternative 1 (current choice):** The find member command can search for members with multiple attributes. + * Pros: Allows users to find members in a more precise manner e.g. Users can find members who are available on Monday and are EXCO members. + * Cons: More complex implementation due to parsing multiple prefixes and chaining predicates, thus this alternative is more prone to bugs. +* **Alternative 2**: The find member command can search for members with only one attribute. + * Pros: Simpler to parse a single prefix and thus less prone to bugs + * Cons: Compromising user experience as finding a member with only one attribute may generate a large list if there are many matching members. + +### Import members feature +The import feature allows users to add and update the details of multiple members without having to repeatedly do so individually. + +#### Implementation + +The import member mechanism is facilitated by `ModelManager` and `SportsPa`. `ModelManager` has access to SportsPA's +data from the `SportsPa` object, from which member data will be read from when the import command is requested by the user. + +**Before going further, here are some terms used that you should take note of:** +* "invalid import" in this context refers to an imported member having the same name as an +existing member AND the same phone number as another existing member. +* "valid import" in this context refers to an imported member not having the same name as an + existing member AND the same phone number as another existing member. + +`ModelManager` implements the following relevant operations: +* `ModelManager#isValidImport(Member)` — Checks if the member being imported is a valid import. +* `ModelManager#hasMember(Member)` — Returns true if a member with the same name or phone as the given member exists in SportsPA. +* `ModelManager#setMember(Member target, Member editedMember)` — Replaces the target member in the list with an edited member. + +Given below is an example usage scenario and how the import mechanism behaves. + +Step 1. The user launches the application for the first time. The user then executes the command addm n/Bob p/12345678, +which adds a member called Bob with a phone number 12345678 into the member list. + +![ImportStep1ObjectDiagram](images/ImportStep1ObjectDiagram.png) -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +Step 2. The user then realises he has many more members to add and wants to use the import command. He prepares a CSV file +called `myFile.csv` to import the members from. -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +![CSVFileScreenShot](images/ImportImplementationCsv.png) -_{more aspects and alternatives to be added}_ +Step 3. The user executes the command import myFile.csv to import the members from the CSV file. The import command first +parses the CSV file using a private method `ImportCommand#parseCsv()`, which returns a list of `Member` objects to be imported. -### \[Proposed\] Data archiving +After which, the command iterates through the list of `Member` objects. Each iteration goes as such: -_{Explain here how the data archiving feature will be implemented}_ +I. A check is done to see if each `Member` is a valid import by calling `ModelManager#isValidImport(Member)`. If it is a +valid import, go to the next step. Else, the current iteration is skipped and a list of skipped members is kept and will be +shown to the user via the GUI.
+In this case, both `Member` objects are valid imports and can be imported. + +II. Then, `ModelManager#hasMember(Member)` is called to check if there are any members in SportsPA with the same name +or phone as the member being imported. If there is such a member in SportsPA, Then the existing member details in SportsPA +will be updated by the imported member details by calling `ModelManager#setMember(Member target, Member editedMember)`. +Else, the imported member is simply added into SportsPA.
+ +In this case, there is 1 member being imported named Bob while there already exists a member called Bob in SportsPA. +So, the existing member, Bob's details will be updated to the details from the CSV file. +As for Amy, the details would be added into SportsPA. + +![ImportStep3ObjectDiagram](images/ImportStep3ObjectDiagram.png) + +The following sequence diagram shows how the import command works. + +![ImportSequenceDiagram](images/ImportSequenceDiagram.png) + +The following activity diagram summarizes what happens when a user executes the import command: + + + +#### Design Considerations: + +**Aspect: how to deal with invalid imports** +* **Alternative 1 (current choice):** Skip the invalid imports and notify the user of the invalid imports. + * Pros: Easy to implement and users will be able to know which imports they need to rectify. + * Cons: Might not be the desired interaction users want. + +* **Alternative 2:** Treat the command as an invalid command. + * Pros: Easy to implement + * Cons: User might want to import the valid imports and the invalid imports might just be an error on their part. -------------------------------------------------------------------------------------------------------------------- @@ -257,71 +615,599 @@ _{Explain here how the data archiving feature will be implemented}_ **Target user profile**: -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +* a NUS Sports *CCA* leader +* has a need to manage a significant number of member’s contacts +* has a need to organise training sessions in multiple facilities whilst adhering to *group size regulations* +* can *type faster than average* +* is reasonably comfortable using *CLI* apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: +* Sports *CCAs* have many members and it can be hard for the leaders to keep track of everyone’s information and + availability to organise training sessions, especially with the current pandemic restrictions. +* SportsPA will help NUS Sports *CCA* leaders to be able to better manage their members’ contacts and attendance as well + as training facilities to better organise *CCA* sessions. ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | - -*{More to be added}* +| Priority | As a …​ | I want to …​ | So that I can…​ +| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- +| `* * *` | sports CCA leader | add a new member | keep track of that member's details +| `* * *` | sports CCA leader | view all the members added | so that I can see the list of members in my club +| `* * *` | sports CCA leader | delete a member | remove details of that member that is no longer in the CCA +| `* * *` | sports CCA leader | add a facility | keep track of that facility's details +| `* * *` | sports CCA leader | view all the facilities added | so that I can see the list of facilities available for use +| `* * *` | sports CCA leader | delete a facility | remove details of that facility that are no longer relevant +| `* * *` | sports CCA leader | record a facility's capacity | see how many members can train there while complying with the group size regulations +| `* * *` | sports CCA leader | record a members' availability | see which members are available to attend a training session held on a certain day +| `* * *` | sports CCA leader | split available members into the facilities | easily plan training sessions for that day while complying with the group size regulations +| `* *` | potential user exploring the app | see the app populated with user data | see how the app works when its in use +| `* *` | new user | see usage instructions | refer to instructions when I forget how to use the app +| `* *` | new user ready to use the app | purge all current data | get rid of sample/experimental data I used when exploring the app +| `* *` | sports CCA leader | edit a member's details | directly make changes to that member's details if required +| `* *` | sports CCA leader | edit a facility's data | quickly update the maximum capacity of each facility when group size regulations have changed +| `* *` | sports CCA leader | find a member by name | locate relevant members and see their details without having to go through the entire list +| `* *` | sports CCA leader | find a facility by location | locate relevant facilities and see their details without having to go through the entire list +| `* *` | sports CCA leader | mark my member's attendance | keep track of who attended today's training session +| `* *` | sports CCA leader | unmark my member's attendance | correct my mistake if I accidentally marked a member who did not attend the session as present +| `* *` | sports CCA leader | see the attendance record of each member | assess their level of commitment +| `* *` | sports CCA leader | manually edit a member's allocation to a facility | accommodate last minute changes or requests made by members +| `* *` | sports CCA leader | tag my members | group them into useful categories, such as members with leadership positions or freshmen +| `* *` | sports CCA leader | filter members by tags | view all members associated to a given category, such as members with leadership positions +| `* *` | sports CCA leader | update members' availabilities in one go | set members' availability at once if they are similar instead of individually updating them one-by-one +| `* *` | user with many members added into the app | sort members by name | locate a member easily +| `* * ` | expert user | create shortcut for commands | personalise command words based on my preferences +| `* *` | sports CCA leader | import members' data | get members to fill in their details first, then add the collated data into the application at one go +| `* *` | sports CCA leader | export allocations into a readable format | share with members their allocated training session and venue +| `*` | user | undo actions | salvage any accidental deletion of data +| `*` | sports CCA leader | hide private contact details | minimise the chance of someone else seeing them by accident +| `*` | graduating sports CCA leader | transfer my app's data to someone else | hand over my responsibilities to the next leader of the club +| `*` | sports CCA leader | archive temporary data | prevent getting distracted by irrelevant data (e.g. facilities that are temporarily unavailable) +| `*` | sports CCA leader | unarchive archived data | view the necessary data again when relevant (e.g. facilities that are back in use) +| `*` | sports CCA leader | delete groups of people | remove all graduating members from the app in one go +| `*` | long time user | create shortcut for tasks | save time on frequently performed tasks ### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is `SportsPA` and the **Actor** is the `user`, unless specified otherwise) + +**Use case: UC01 - Get help** + +**MSS** + +1. User requests for help +2. SportsPA displays instructions on accessing the help page +3. User navigates to the help page + + Use case ends. + +**Use case: UC02 - Add a member** + +**MSS** + +1. User requests to add a member into SportsPA +2. SportsPA adds the member + + Use case ends. + +**Extensions** + +* 1a. The given field(s) are invalid + + * 1a1. SportsPA shows an error message + + Use case resumes from step 1. + +* 1b. The given member already exists in SportsPA + + * 1b1. SportsPA shows an error message informing the user about the duplicate + + Use case ends + +**Use case: UC03 - List all members** + +**MSS** + +1. User requests to list members +2. SportsPA shows a list of all the members + + Use case ends. + +**Extensions** + +* 1a. The member list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +**Use case: UC04 - Search for members** + +**MSS** + +1. User requests to search for members by the given field(s) +2. SportsPA shows the list of members matching the given request + + Use case ends. + +**Extensions** + +* 1a. The given field(s) are invalid + + * 3a1. SportsPA shows an error message + + Use case resumes from step 1. + +* 1a. No members have fields matching the given request + + * 1a1. SportsPA informs user + + Use case ends. -**Use case: Delete a person** +**Use case: UC05 - Delete a member** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to delete a specific member in the list +3. SportsPA deletes the member + Use case ends. + +**Extensions** + +* 1a. The list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 2a. The given index is invalid + + * 2a1. SportsPA shows an error message + * 2a2. User enters a valid index + + Use case resumes from step 2. + +**Use case: UC06 - Edit a member's details** + +**MSS** + +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to edit the details of specific member in the list +3. SportsPA edits the details of the member + + Use case ends. + +**Extensions** + +* 1a. The list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 2a. The given index is invalid + + * 2a1. SportsPA shows an error message + + * 2a2. User enters a valid index + + Use case resumes from step 2. + +* 2b. The given field(s) are invalid + + * 2b1. SportsPA shows an error message + + * 2a2. User enters the valid field(s) + + Use case resumes from step 2. + +**Use case: UC07 - Sort all members** + +**MSS** + +1. User requests to sort all the members by a field +2. SportsPA sorts the members accordingly + Use case ends. **Extensions** -* 2a. The list is empty. +* 1a. The given field is invalid + + * 1a1. SportsPA shows an error message + + Use case ends. + +**Use case: UC08 - Set member(s) availability** + +**MSS** + +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to set availability of specific member(s) in the list +3. SportsPA updates the availability of the given member(s) + + Use case ends. + +**Extensions** + +* 1a. The list is empty + + * 1a1. SportsPA shows an error message Use case ends. -* 3a. The given index is invalid. +* 2a. One or more of the given member index is invalid - * 3a1. AddressBook shows an error message. + * 2a1. SportsPA shows an error message + + * 2a2. User enters valid index - Use case resumes at step 2. + Use case resumes from step 2. -*{More to be added}* +* 2b. The given availability is invalid -### Non-Functional Requirements + * 2b1. SportsPA shows an error message + + * 2b2. User enters a valid availability + + Use case resumes from step 2. + +**Use case: UC09 - Mark attendance of members** + +**MSS** + +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to mark the attendance of specific member(s) in the list +3. SportsPA marks the attendance of the specified member(s) + + Use case ends. + +**Extensions** + +* 1a. The list is empty -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. + * 1a1. SportsPA shows an error message -*{More to be added}* + Use case ends. + +* 2a. One or more of the given member index is invalid + + * 2a1. SportsPA shows an error message + + * 2a2. User enters the valid index + + Use case resumes from step 2. + +**Use case: UC10 - Unmark attendance of members** + +This use case is similar to that of mark attendance (UC09). + +**Use case: UC11 - Import member details from a CSV file** + +**MSS** +1. User requests to import member details from a CSV file +2. SportsPA adds all members in the CSV file + + Use case ends. + +**Extensions** + +* 1a. The CSV file does not exist + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 1b. The content of CSV file is not in the valid format + + * 1b1. SportsPA shows an error message + + Use case ends. + +* 1c. There are some imported members that have the same name or same phone + + * 1c1. SportsPA updates the details of those members using data from the CSV file + + Use case ends. + +**Use case: UC12 - Clear all entries in member list** + +**MSS** + +1. User requests to clear all entries in member list +2. SportsPA deletes all the existing members in the member list + + Use case ends. + +**Extensions** + +* 1a. The member list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +**Use case: UC13 - Add a facility** + +**MSS** + +1. User requests to add a facility into SportsPA +2. SportsPA adds the facility + + Use case ends. + +**Extensions** + +* 1a. SportsPA detects missing or invalid field(s) + + * 1a1. SportsPA shows an error message + + * 1a2. User enters valid field(s) + + Use case resumes from step 1. + +**Use case: UC14 - List all facilities** + +**MSS** + +1. User requests to list facilities +2. SportsPA shows a list of all the facilities + + Use case ends. + +**Extensions** + +* 1a. The facility list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +**Use case: UC15 - Search for facilities** + +**MSS** + +1. User requests to find facilities by location(s) +2. SportsPA shows the list of facilities whose locations match the request + + Use case ends. + +**Extensions** + +* 1a. The facility list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 1b. No facilities have locations matching the request + + * 1b1. SportsPA informs user + + Use case ends. + +**Use case: UC16 - Delete a facility** + +**MSS** + +1. User requests to list facilities (UC14) + or search for facilities (UC15) +2. User requests to delete a specific facility in the list +3. SportsPA deletes the facility + + Use case ends. + +**Extensions** + +* 1a. The facility list is empty + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 2a. The given index is invalid + + * 2a1. SportsPA shows an error message + + * 2a2. User enters the valid index + + Use case resumes from step 2. + +**Use case: UC17 - Edit a facility's details** + +**MSS** + +1. User requests to list facilities (UC14) + or search for facilities (UC15) +2. User requests to edit the details of specific facility in the list +3. SportsPA edits the details of the facility + + Use case ends. + +**Extensions** + +* 1a. The list is empty + + Use case ends. + +* 2a. The given index is invalid + + * 2a1. SportsPA shows an error message + + * 2a2. User enters the valid index + + Use case resumes from step 2. + +* 2b. SportsPA detects invalid field(s) + + * 2b1. SportsPA shows an error message + + * 2b2. User enters the valid field(s) + + Use case resumes from step 2. + +**Use case: UC18 - Split members into facilities** + +**MSS** + +1. User requests to split available members into the facilities on a specified day +2. SportsPA shows the allocation results + + Use case ends. + +**Extensions** + +* 1a. SportsPA detects insufficient capacity to allocate all available members + + * 1a1. SportsPA shows an error message + + Use case ends. + +* 1b. SportsPA detects no available members + + * 1b1. SportsPA shows an error message + + Use case ends. + +**Use case: UC19 - Deallocate a member from a facility** + +**MSS** + +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to list facilities (UC14) + or search for facilities (UC15) +3. User requests to deallocate a specified member from a specified facility on a specified day. +4. SportsPA deallocates the specified member from the specified facility on a specified day. + + Use case ends. + +**Extensions** + +* 3a. One or more of the given indices are invalid + + * 3a1. SportsPA shows an error message + + * 3a2. User enters the valid index + + Use case resumes from step 3. + +* 3b. The given day is invalid + + * 3b1. SportsPA shows an error message + + * 3b2. User enters a valid day + + Use case resumes from step 3. + +* 3c. The specified member is not allocated to the specified facility + + * 3c1. SportsPA shows an error message + + * 3c2. User enters a member that is allocated to that facility + + Use case resumes from step 3. + +**Use case: UC20 - Allocate a member to a facility** + +**MSS** +1. User requests to list members (UC03) + or search for members (UC04) +2. User requests to list facilities (UC14) + or search for facilities (UC15) +3. User requests to allocate a specified member to a specified facility on a specified day +4. SportsPA allocates the specified member to the specified facility on a specified day + + Use case ends. + +**Extensions** + +* 3a. One or more of the given indices are invalid + + * 3a1. SportsPA shows an error message + + * 3a2. User enters the valid index + + Use case resumes from step 3. + +* 3b. The given day is invalid + + * 3b1. SportsPA shows an error message + + * 3b2. User enters a valid day + + Use case resumes from step 3. + +* 3c. The specified member is already allocated to the specified facility. + + * 3c1. SportsPA shows an error message + + * 3c2. User enters a member that is not allocated to that facility + + Use case resumes from step 3. + +**Use case: UC21 - Export facility details and member allocations** + +**MSS** +1. User requests to export facility details and member allocations +2. SportsPA exports the facility details and member allocations to a CSV file + + Use case ends. + +**Use case: UC22 - Clear all entries in facility list** + +**MSS** + +1. User requests to clear all entries in facility list +2. SportsPA deletes all facilities + + Use case ends. + +**Extensions** + +* 1a. The facility list is empty + + * 1a1. SportsPA shows an error message + + Use case ends + +**Use case: UC23 - Exiting the program** + +**MSS** + +1. User requests to exit the program +2. SportsPA terminates + + Use case ends. + +### Non-Functional Requirements + +1. Should work on any *mainstream OS* as long as it has Java 11 or above installed +2. Should be able to hold up to 1000 entries (members and facilities) without a noticeable sluggishness in performance + for typical usage +3. Should be able to process and execute user commands within 3 seconds +4. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be + able to accomplish most of the tasks using commands faster than using a mouse ### Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **Graphical User Interface (GUI)**: A user interface that includes graphical representation like buttons and icons for + users to interact with +* **Command Line Interface (CLI)**: A text-based user interface that the user interacts with by typing in commands +* **JSON** : JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. +* **Group size regulations**: Maximum allowable group size for sporting activities as specified by Covid-19 regulations +* **Fast typists**: Types faster than 40wpm (words per minute) +* **Alias**: A shortcut name for any command in SportsPA +* **CCA**: In Singapore, a co-curricular activity (CCA), is a non-academic activity that students take part in. -------------------------------------------------------------------------------------------------------------------- @@ -338,40 +1224,193 @@ testers are expected to do more *exploratory* testing. 1. Initial launch - 1. Download the jar file and copy into an empty folder + 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 1. Double-click the jar file + Expected: Shows the GUI with a set of sample contacts. The window size may not be + optimum. -1. Saving window preferences +2. Saving window preferences - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. + 1. Resize the window to an optimum size. Move the window to a different location. Close the window. - 1. Re-launch the app by double-clicking the jar file.
+ 1. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. + + +### Adding a member + +1. Adding a new member. + + 1. Prerequisites: The list of members does not already contain members with the same names and/or phone numbers as those in the test + cases below. + + 2. Test case: addm n/Bob p/12345678
+ Expected: New member with the same name and phone number is added to the bottom of the list + of members. The details of the member are shown in the status message. + + 3. Test case: addm n/Ch@rlie p/45678
+ Expected: No new member added. Error details are shown in the status message. + + 4. Test case: addm n/Adam p/0b234
+ Expected: Similar to previous. + + 5. Test case: addm n/Sam p/87654321 followed by addm n/Sam p/23456789
+ Expected: After the first command, a new member called Sam with the phone number 87654321 + is added to the list of members. After the second command, no member is added and error details + are shown in the status message. + + 6. Test case: addm n/John p/98765432 followed by addm n/Jane p/98765432
+ Expected: After the first command, a new member called John with the phone number 98765432 is added to the list of members. + After the second command, no member is added and error details are shown in the status message. + + + +### Deleting a member + +1. Deleting a member while all members are being shown. + + 1. Prerequisites: List all members using the listm command. One or more members are in the list. + + 2. Test case: deletem 1
+ Expected: First member is deleted from the list. Details of the deleted member shown in the status message. + + 3. Test case: deletem 0
+ Expected: No member is deleted. Error details shown in the status message. Status bar remains the same. + + 4. Other incorrect delete commands to try: deletem, deletem x, ... (where x is larger than the list size)
+ Expected: Similar to previous. + + +### Editing a member + +1. Edit the details of a member that is being shown in the list. -1. _{ more test cases …​ }_ + 1. Prerequisites: List all members using the listm command. One or more members are in the list and the list does + not contain any members with the same names and/or phone number as those in the test cases below. + + 2. Test case: editm 1 n/Adam
+ Expected: First member's name is changed to Adam. Details of the edited member is shown in the status message. + + 3. Test case: editm 1 n/@dam
+ Expected: No member details are changed. Error details are shown in the status message. + + 4. Other incorrect delete commands to try: editm, editm x n/Bob p/45678, ... + (where x is larger than the list size)
+ Expected: Similar to previous. -### Deleting a person -1. Deleting a person while all persons are being shown +### Setting member availability +1. Set the availability of one or more members + + 1. Prerequisites: List all members using the listf command. One or more members are in the list. + + 2. Test case: setm 1 2 3 d/1 2
+ Expected: The availability of the first 3 members are changed to Monday and Tuesday. The names of members + that had their availability changed are shown in the status message. + 3. Test case: setm 1 2 3
+ Expected: No change in the members' availability. Error details are shown in the status message. + 4. Other incorrect delete commands to try: setm, setm x d/1, ... + (where x is larger than the list size)
+ Expected: Similar to previous. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. +### Adding a facility - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +1. Adding a new facility. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. + 1. Prerequisites: The list of facilities does not already contain facilities with the same names and locations + as those in the test cases below. -1. _{ more test cases …​ }_ + 2. Test case: addf n/Court 1 l/Sports Hall t/1500 c/5
+ Expected: New facility called Court 1 at Sports Hall at 3pm with a capacity of 5 is added to the bottom of the list + of facilities. + + 3. Test case: addf n/Court #1 l/Sports Hall t/1800 c/5
+ Expected: No new facility added. Error details are shown in the status message. + + 4. Test case: addf n/Court 1 p/Sports H@ll t/1100 c/5
+ Expected: Similar to previous. + + 5. Test case: addf addf n/Court 2 l/Sports Hall t/1200 c/5 followed by addf n/Court 2 l/Sports Hall t/1600 c/5
+ Expected: After the first command, a new facility called Court 2 at Sports Hall at 12pm with capacity of 5 + is added to the list of facilities. After the second command, no facility is added and error details + are shown in the status message. + +### Deleting a facility + +The test cases are similar to those of [Deleting a member](#deleting-a-member). + +### Editing a facility + +1. Edit the details of a facility that is being shown in the list. + + 1. Prerequisites: List all members using the listf command. One or more members are in the list and the list does + not contain any members with the same names as those in the test cases below. + + 2. Test case: editf 1 n/Court 3 t/1800
+ Expected: First facility's name is changed to Adam and time is changed to 6pm. + Details of the edited facility is shown in the status message. + + 3. Test case: editf 1 t/9999
+ Expected: No member details are changed. Error details are shown in the status message. + + 4. Other incorrect delete commands to try: editf, editf x n/Court 1 l/Sports Hall t/1800 c/5, ... + (where x is larger than the list size)
+ Expected: Similar to previous. + +### Splitting members into facilities + +1. Allocating all the members into the allocation maps of the facilities on a given day. + + 1. Test case: split 1
+ Expected: All members that have Monday as one of their available days will be allocated to a facility if + there is sufficient capacity. Their names will be shown in the facilities' allocation maps under Monday. + + 2. Test case: split
+ Expected: No members are allocated to any facility. Error details are shown in the status message. + ### Saving data -1. Dealing with missing/corrupted data files +1. Dealing with missing data files + + 1. Navigate to [JAR File location]/data and move the file sportspa.json into another folder. + 2. Run SportsPA. + 3. SportsPA will start up with sample members and facilities being present. + +2. Dealing with corrupted data files + 1. Navigate to [JAR File location]/data and rename sportspa.json to sportspa.txt. + 2. Open sportspa.txt and delete the first { in the file and close the file. + 3. Rename sportspa.txt back to sportspa.json. + 4. Run SportsPA. + 5. SportsPA will start up without any data being present. + +-------------------------------------------------------------------------------------------------------------------- + +## **Appendix: Effort** + +Our team has put in significant effort in evolving AB3 (Address Book Level 3) into SportsPA. In the following subsections, we list some notable contributions to the project as well as challenges that we faced during the course. + +### Contributions + +1. **Handling two entities, Members and Facilities** +
AB3 handles only one entity type, Person, whereas we introduced another entity type, Facility. This introduced the complexity of handling the connection and interaction between the two entities. Since Facility objects kept references to Member objects, certain measures had to be implemented to maintain consistency. For example, Facility objects have to respond to changes to Member objects they have reference to, otherwise there may be a conflict in the data presented to the user. +

+2. **Enhancing User Experience for our Target Users** +
Our team also put a lot of thought into designing and implementing features that would specifically enhance our target users’ user experience. Such features include the import and export command. Since this project is restricted to only one end user, we included this feature to help facilitate the communication between our target users, sports CCA leaders, and their members. Another feature is the alias command, which is a fairly staple feature for users who are familiar with CLI applications, giving our users the flexibility to personalise the commands. +

+3. **Improving Documentation** +
The team significantly improved AB3’s documentation, especially the User Guide. We completely revamped the User Guide to become more reader-friendly and reader-focused, taking on a more welcoming tone and including more useful tips, notes and warnings for some of the features. We also added instructions on how to navigate and understand the documentation. Additionally, the Developer Guide was adapted to include diagrams, implementation details, design considerations, user stories and use cases to become more relevant to our project. + +### Challenges Faced +1. **Handling Duplicates** +
One of the more noteworthy challenges we faced was to decide whether we should allow duplicate members in SportsPA. Initially, SportsPA did not allow duplicate names, but allowed duplicate phone numbers. This problem surfaced from the PED when a reviewer mentioned that users may need to add members with the same name. By then, many of our commands were finalised and just a little tweak to the method could break many method chains as multiple commands were using the same mechanism. Our team had to perform an in-depth analysis on the dependencies of that method to avoid such complications. +
Our team ultimately decided that SportsPA will not support duplicate members, that is, if they have the same name and/or phone number. We weighed the pros and cons of both alternatives and discovered that preserving the no-duplicates characteristic inherited from AB3 would require significantly less changes to our existing commands. Even though we eventually settled with disallowing duplicates, an additional duplicate phone number check was implemented and the import function had to be amended to support these changes. +

+2. **Coupling of Features** +
Another challenge was that some features were dependent on the implementation of other features, so it was difficult to ensure that these implementations would complement each other and work as expected when different members worked on these different features. For example, one member implemented the split feature, while another worked on the setm features which sets the availability of the members. However, the split feature depends on setm in order to function properly. Hence, when merging these features together, it resulted in the split feature not working as expected, and thus needed more modifications and effort to integrate the features. - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +### Final Thoughts +Overall, the difficulty of the project was high due to the complexity of some of our features. However, with the combined efforts of every team member, we were able to release the final version of the product that we can be proud of. -1. _{ more test cases …​ }_ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..a004ac14d15 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,192 +1,884 @@ --- layout: page title: User Guide +nav-text: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +

+
+

+ +Welcome to the SportsPA User Guide! + + +SportsPA is a desktop application targeted for use among NUS sports CCA Leaders. It manages membership and +training sessions for NUS sports CCAs. SportsPA allows you to keep track of details of your members as well as +facilities to hold training sessions at. With SportsPA, you will be able to quickly organise training sessions as we +help you allocate available members into the various facilities according to their specified capacity. + +SportsPA mainly uses a CLI (Command Line Interface), meaning that you simply have to type in commands to use it. This user +guide provides an in-depth documentation on all the commands available. If this is your first time using SportsPA, +we also provide a [quick start guide](#quick-start) that demonstrates the end-to-end setup process to get you started. + +_____________________________________________________________________________________________________________ + + + +Table of Contents + * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- +
-## Quick start +_____________________________________________________________________________________________________________ +## How to use the User Guide -1. Ensure you have Java `11` or above installed in your Computer. +You can click on the links in the [Table of Contents](#table-of-contents) to quickly navigate to your desired location in +this User Guide. A link to return to the [Table of Contents](#table-of-contents) is also provided at the end of every section. +

+The table below summarizes the meaning of the icons and text styles used throughout this User Guide. +
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +Icon / Text Style | Description +----------------- | ------------------ +**bold** | Highlights important information such as components of SportsPA or constraints of command parameters +`keyboard` | Represents commands in the format that you should follow when typing them +UPPER_CASE | Represents parameters to be supplied by you for commands +[link](#table-of-contents) | Represents links that can be clicked on to navigate to a relevant section of the User Guide or a different website +**:information_source: Notes:** | Represents important information regarding commands such as their format and constraints +**:exclamation: Caution:**| Represents warnings for actions that can result in the unintentional and irreversible removal of data or affect the result of a previously executed command +**:bulb: Tip:**| Represents useful tips that we would like to share +:camera: | Signifies that a screenshot of the outcome of the command is provided below -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +[Back to Table of Contents](#table-of-contents) -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) +
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: +_____________________________________________________________________________________________________________ +## Quick Start - * **`list`** : Lists all contacts. +1. Ensure you have Java `11` or above installed on your Computer. If you are unsure of how to do so, refer to the [FAQ](#faq). - * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. +2. Download the latest `SportsPA.jar` from [here](https://github.com/AY2122S1-CS2103T-W12-1/tp/releases/tag/v1.4). - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +3. Copy the file to the folder you want to use as the _home folder_ for SportsPA. - * **`clear`** : Deletes all contacts. +4. Double-click the file to start the app. You should see a graphical interface similar to the image below appear in a few seconds. SportsPA + provides some sample data for you to play around with and familiarize yourself with the commands. +

+ +

+ + If you are on macOS and encounter the below issue when launching SportsPA, follow [this](https://support.apple.com/en-sg/guide/mac-help/mh40616/mac). - * **`exit`** : Exits the app. +

+ +

-1. Refer to the [Features](#features) below for details of each command. +[Back to Table of Contents](#table-of-contents) --------------------------------------------------------------------------------------------------------------------- +_____________________________________________________________________________________________________________ + +## Using SportsPA's Interface + +While SportsPA has a GUI (Graphical User Interface) to display data and messages, you interact with it mainly +by typing. The figure below provides an overview of the key components in our interface.

+![Gui](images/Gui.png) + +Typically, to execute a command, this is how you interact with the interface: + +1. Type the desired command into the **Command Window** and press **Enter** on your keyboard. +2. The results of the command (or any warning messages generated) are displayed in the **Result Window**. +3. Any changes made to the data will be reflected accordingly in the **List Window**. + +SportsPA stores two lists - **Members** and **Facilities**, which you can freely switch between by clicking on the respective **Tabs**. Alternatively, whenever you execute a member-specific or facility-specific command, SportsPA automatically switches to the relevant tab for you. + +Here are some example commands you can try: + +* **`help`** : Displays help window. + +* **`listm`** : Lists all members. + +* **`listf`** : Lists all facilities. + +* **`addf n/Court 1 l/University Sports Centre t/1700 c/10`** : Adds the facility named `Court 1` to the facilities + list. + +* **`deletem 3`** : Deletes 3rd member in the members list. + +* **`exit`** : Exits the app. +[Back to Table of Contents](#table-of-contents) + +_____________________________________________________________________________________________________________ + +## Command Syntax +Before we start, let's take a look at what a typical command in SportsPA comprises of. + +Component | Description +----------------- | ------------------ +Command Word | The keyword that represents a command in SportsPA +Preamble | The keyword that represents an `INDEX` / `DAY` +Prefix | The keyword preceding the parameter(s) +Parameter | The argument proceeding the prefix + +To illustrate the above, we will use the edit member command:
+`editm 1 n/John Doe p/92315540 d/2 3 t/exco`
+* `editm` is the command word +* `1` is the preamble representing the index of the member to edit +* `n/` `p/` `d/` `t/` are the prefixes +* `John Doe`, `92315540`, `2 3`, `exco` are the parameters
+ +The add member command also follows a similar format, but without the preamble:
+`addm n/John Doe p/92315540 t/exco` + +For your easy reference, we have prepared a summary of prefixes and their required parameters below. + +Member-Specific prefixes: + +Prefix | Parameter | Requirements +----------------- | ------------------|------------------ +`p/` | `PHONE` | Minimum of 3 digits and maximum of 15 digits +`d/` | `DAY(S)` | Integer, 1-7 only, 1 represents Monday, 2 represents Tuesday etc. +`t/` | `TAG` | Maximum of 20 alphanumeric characters and spaces +`tda/` | `TODAY_ATTENDANCE` | `true` for present or `false` for absent +`tta/`| `TOTAL_ATTENDANCE` | Integer, non-negative only +`by/` | `SORT_ORDER` | `name` or `tag` only + +Facility-Specific prefixes: + +Prefix | Parameter | Requirements +-------- | --------- | ---------------- +`l/` | `LOCATION` | Maximum of 50 alphanumeric characters and spaces +`c/` | `CAPACITY`| Integer, 1-50 only +`t/` | `TIME` | 24-hour format e.g., 1400 + +General prefixes: + +Prefix | Parameter | Requirements +-------- | --------- | ---------------- +`n/` | `NAME` | Maximum of 50 alphanumeric characters and spaces +`s/` | `SHORTCUT` | Not an existing command word in SportsPA, 1 word only +`cw/` | `COMMAND_WORD` | Existing command words in SportsPA + +[Back to Table of Contents](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------- ## Features +This section documents all the commands available in SportsPA, guiding you through its function, format, example usages +and any other noteworthy tips. **Note that examples ending with :camera: means that a screenshot of the outcome is provided below.** + +For a summary of all the prefixes and parameters, refer to the [Command Syntax](#command-syntax). + +For a summary of all the commands, refer to the [Command Summary](#command-summary). If this +is your first read, do go through the following notes about the command format to help you better understand the +documentation. +
**:information_source: Notes about the command format:**
+* Characters with the slash symbols are used to indicate the type of parameter supplied by the user. +
e.g. in `addm n/NAME p/PHONE_NUMBER`, `n/` and `p/` are the symbols used before entering a parameter for `NAME` + and `PHONE_NUMBER` respectively. + * Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. + e.g. in `deletem INDEX`, `INDEX` is a parameter which can be used as `deletem 1`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. + e.g. `n/NAME p/PHONE_NUMBER [d/DAY(S)] [t/TAG]...` can be used as `n/Ben p/91111111 d/1` or as `n/John p/91111111` or + as `n/John p/91111111 t/exco`. + +* Items with `...` after them can be used multiple times including zero times, by repeating the character with slash symbol, + if present, and the parameter.
+ e.g. `[t/TAG]...` can be used as ` ` (i.e. 0 times), `t/exco`, or `t/exco t/y2`, etc. + +* Parameters with `(S)` at the end, can be supplied one or more times without repeating the characters with slash + symbols.
+ e.g. `d/DAY(S)` can be used as `d/1`, `d/1 2`, etc. -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +* Parameters with the slash symbols can be in any order.
+ e.g. if the command specifies `n/NAME l/LOCATION`, `l/LOCATION n/NAME` is also acceptable. -* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. +* If parameters following slash symbols are expected only once in the command but you specified it multiple times, only the last occurrence of + the parameter wll be taken.
+ e.g. for `p/PHONE`, if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +* Parameters without slash symbols can only be specified once, and must follow the order of the command format.
+ e.g. for `INDEX`, if you specify `1 1`, it is invalid. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters for commands that do not take in parameters (such as `help`, `listf`, `clearm` and `exit`) will + be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
-### Viewing help : `help` +### Member-Specific Features -Shows a message explaning how to access the help page. +#### Adding a member: `addm` -![help message](images/helpMessage.png) +Adds a member to your members list. -Format: `help` +Format: `addm n/NAME p/PHONE_NUMBER [d/DAY(S)] [t/TAG]...` + +* `DAY(S)` specifies the days when the member is available for that week +* `DAY(S)` should be provided as a numerical index, where `1` represents Monday, `2` represents Tuesday … and `7` represents + Sunday and **must be separated by a single space** e.g. `d/1 2 3` +* Members added without `DAY(S)` will have no available days by default + +
+ +**:information_source: Note:** You will not be able to add members with the same `NAME` or `PHONE_NUMBER` as another existing member into the member list as they are +considered duplicates. +
+ +Examples: + +* `addm n/Bob Chia p/91228372` adds Bob Chia to the member list with zero available days by default +* `addm n/John Tan p/91234567 d/1 3 5` adds John Tan to the member list and indicates his availability on Monday, Tuesday and + Friday +* `addm n/Harry Li p/91234567 d/1 t/exco t/y2` adds Harry Li to the member list, indicates his availability on Monday and tags him as 'exco' and 'y2' :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + +#### Listing all members: `listm` + +Shows a list of your members. + +Format: `listm` + +[Back to Table of Contents](#table-of-contents) + +#### Deleting a member: `deletem` + +Deletes a member from your member list. + +Format: `deletem MEMBER_INDEX` + +* Deletes the member at the specified `MEMBER_INDEX` +* The index refers to the index number shown in the **currently** displayed member list +* The index **must be a positive integer** 1, 2, 3, …​ + +
+ +**:information_source: Note:** Deleting a member will automatically remove them from any facilities they were allocated to. +
+ +Examples: + +* `listm` followed by `deletem 2` deletes the member at index 2 of the member list +* `findm n/John` followed by `deletem 1` deletes the first member in the results of the `findm` command + +[Back to Table of Contents](#table-of-contents) + +#### Editing a member: `editm` + +Edits an existing member from your member list. + +Format: `editm MEMBER_INDEX [n/NAME] [p/PHONE_NUMBER] [t/TAG]...` + +* Edits the member at the specified `MEMBER_INDEX` +* `MEMBER_INDEX` refers to the index number shown in the displayed member list +* `MEMBER_INDEX` **must be a positive integer** 1, 2, 3… +* At least one of the optional fields must be provided +* Existing values will be updated to the input values +
:exclamation: **Caution:** +Editing any fields of a member will automatically remove them from any facilities they were allocated to. If necessary, you can manually allocate the edited member +back to the facility (see [Allocating a member to a facility](#allocating-a-member-to-a-facility-allocate)) or perform the `split` command again (see [Splitting members into facilities](#splitting-members-into-facilities-split)) +
-### Adding a person: `add` +
-Adds a person to the address book. +**:bulb: Tip 1:** Didn't tag a member when you added them? It's not too late, just tag them using this command! +Similarly, use `t/` (nothing supplied) to remove all tags from a member. +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +
-
:bulb: **Tip:** -A person can have any number of tags (including 0) +**:bulb: Tip 2:** If you are looking to edit a member's availability, see [Setting member availability](#setting-member-availability-setm).
Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` -### Listing all persons : `list` +* `editm 1 n/Jonathan t/` edits the name of the 1st member to be `Jonathan` and removes all its tags +* `editm 2 n/Jonathan p/93837283` edits the name and phone number of the 2nd member to be `Jonathan` and `93837283` + respectively +* `editm 2 t/exco t/y1` edits the 2nd member to have tags `exco` and `y1` :camera: -Shows a list of all persons in the address book. +

+ +

-Format: `list` +[Back to Table of Contents](#table-of-contents) -### Editing a person : `edit` +#### Finding a member: `findm` +If you are looking for specific members, `findm` lets you find and filter members based on any of the +specified field keywords. -Edits an existing person in the address book. +Format: `findm [n/NAME] [p/PHONE] [d/DAY(S)] [tda/TODAY_ATTENDANCE] [tta/TOTAL_ATTENDANCE] [t/TAG]...` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +* **At least one** of the optional fields **must** be provided +* `findm` is **case-insensitive**. e.g. `n/john` will match `John` +* Only **full words** will be matched e.g. `n/Johnny` will not match `John` +* For `[t/TAG]...`, members matching **at least one tag** will be listed (i.e. OR search) +
e.g. `findm t/exco t/y2` will list members with tags `exco` `y2`, and also members with only one matching tag `y2` or `exco` +* If multiple field keywords are supplied, only members that match **all** the given field keywords will be listed (i.e. AND search) +
e.g. `findm n/Amy t/exco` will match `Amy` with the tag `exco` but not `John` with the tag `exco` + +
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +**:information_source: Note:** For the parameter `d/DAY(S)`, if the member is not available on any one of the specified days, +they will not be shown in the filtered list (i.e members have to be available on all specified days in order for them to be +shown in the list). +
+ +
+ +**:bulb: Tip:** Member list is too large? Simply use this command to narrow down the members you are looking for! +
Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. -### Locating persons by name: `find` +* `findm n/John` returns `john` and `John Doe` +* `findm t/exco t/y2` returns members with either tags `exco` `y2`, or both :camera: -Finds persons whose names contain any of the given keywords. +

+ +

-Format: `find KEYWORD [MORE_KEYWORDS]` +[Back to Table of Contents](#table-of-contents) -* The search is case-insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +#### Sorting member list: `sortm` + +Shows a list of all members, sorted by their names or their tags. + +Format: `sortm by/SORT_ORDER` +* When sorting by names, members with lexicographically smaller names are displayed first +* When sorting by tags, members with more tags are displayed first Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `sortm by/name` returns a member list sorted alphabetically by their names +* `sortm by/tag` returns a member list sorted by the number of tags -### Deleting a person : `delete` +[Back to Table of Contents](#table-of-contents) -Deletes the specified person from the address book. +#### Setting member availability: `setm` -Format: `delete INDEX` +The availability of your members can change frequently. Thus, instead of having to individually edit your members' availability, use +`setm` to set the availability of given member(s) at one go. -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +Format: `setm MEMBER_INDEX [MORE_INDICES]... d/DAY(S)` + +* Sets the availability of the member(s) at the specified `MEMBER_INDEX` and `MORE_INDICES` to be the specified `DAY(S)` +* `DAY(S)` **must be separated by a single space** e.g. `d/1 2 3` +* `MEMBER_INDEX` and `MORE_INDICES` refer to the index number(s) shown in the displayed member list +* `MEMBER_INDEX` and `MORE_INDICES` **must be positive integers** 1, 2, 3, …​ +* `MORE_INDICES` **must be separated by a single space** e.g. `1 2 3` + +
:exclamation: **Caution:** +Setting the availability of a member will automatically remove them from any facilities they were allocated to. +This action is **irreversible**. +
+ +
+ +**:information_source: Note:** All indices provided must be in the range of the displayed member list. Otherwise, +the command will be considered invalid and none of the member's availability will be set. +
Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. -### Clearing all entries : `clear` +* `findm n/John` followed by `setm 2 d/1` sets the availability of the member at index 2 in the results of the `findm` + command to be Monday +* `listm` followed by `setm 5 d/1 2` sets the availability of the member at index 5 in the member list to be Monday and + Tuesday +* `listm` followed by `setm 1 2 3 d/3 4` sets the availability of the first three members in the member list to be Wednesday and Thursday :camera: -Clears all entries from the address book. +

+ +

-Format: `clear` +[Back to Table of Contents](#table-of-contents) -### Exiting the program : `exit` +#### Marking attendance of members: `mark` -Exits the program. +During a training session, you can mark the attendance of members present (represented by a tick) with this command. -Format: `exit` +Format: `mark MEMBER_INDEX [MORE_INDICES]...` + +* Marks the member(s) at the specified `MEMBER_INDEX` and `MORE_INDICES` in the currently displayed member list as present +* `MEMBER_INDEX` and `MORE_INDICES` refers to the index number of the member in the displayed members list +* `MEMBER_INDEX` and `MORE_INDICES` **must be positive integers** 1, 2, 3 …​ +* `MORE_INDICES` **must be separated by a single space** e.g. `1 2 3` + +
+ +**:bulb: Tip:** After you have marked attendance for today, use the `cleara` command +(see [Clearing all attendance for today](#clearing-all-attendance-for-today-cleara)) to reset everyone's attendance today and save +their total attendance so that you can use the mark command again to take attendance for another day. +
+ +Examples: + +* `mark 1 2` marks the attendance of the members at indices 1 and 2 in the currently displayed list as present :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + +#### Unmarking attendance of members: `unmark` + +Unmarks attendance of members marked as present, represented by a cross. + +Format `unmark MEMBER_INDEX [MORE_INDICES]...` + +* Unmarks the member(s) at the specified `MEMBER_INDEX` and `MORE_INDICES` in the members list as present +* `MEMBER_INDEX` and `MORE_INDICES` refers to the index number of the member in the displayed members list +* `MEMBER_INDEX` and `MORE_INDICES` **must be positive integers** 1, 2, 3 …​ +* `MORE_INDICES` **must be separated by a single space** e.g. `1 2 3` + +Examples: +* `unmark 1 2` unmarks the attendance of members at indices 1 and 2 as not present + +[Back to Table of Contents](#table-of-contents) + +#### Clearing all attendance for today: `cleara` + +At the end of a training session, you can use this command to finalise and update the member's attendance records, then unmark all member's attendance to prepare for the next training session. + +Format: `cleara` + +
:exclamation: **Caution:** +All attendance data will be unmarked immediately and total attendance of those who were present will be incremented after this command is executed. This action is **irreversible**. +
+ +[Back to Table of Contents](#table-of-contents) + +#### Importing multiple members using a CSV file: `import` + +When you want to add or update the details of multiple members in one go, you can import data from +a CSV (comma-separated values) file using `import`. + + +Format: `import CSV_FILE_PATH` + +* Data imported from the CSV file **must** have 4 headers in this order: + 1. Name + 2. Phone number + 3. Availability + 4. Tags +* In the CSV file, parameters' formats follows that of the `addm` command +(see [Adding a Member](#adding-a-member-addm)) +* You can add multiple tags by separating each tag with a comma in the CSV file. +* `CSV_FILE_PATH` should be relative to the JAR file location + +
:exclamation: **Caution:** +If there are duplicate members (i.e. members with the same name or same phone number) being imported using the CSV file, +the details from the CSV file will overwrite the existing details, except for the attendance data. +This action is **irreversible**. +
+ +
+ +**:information_source: Note:** If any members being imported is deemed invalid, those members will be skipped. +A member being imported will be invalid if the member being imported has the same name an existing member in SportsPA +**AND** has the same phone as another existing member in SportsPA. + +
+ +Examples: + +* `import myFile.csv` imports member data from the CSV file in `[JAR_file_location]/myFile.csv` :camera: + +![importExample](images/importExample.png) + +[Back to Table of Contents](#table-of-contents) + +#### Clearing all entries in member list: `clearm` + +Clears all members from your member list. + +Format: `clearm` + +
:exclamation: **Caution:** +All member data will be removed immediately after this command is executed. This action is **irreversible**. +
+ +[Back to Table of Contents](#table-of-contents) + +
-### Saving the data +### Facility-Specific Features -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +#### Adding a facility: `addf` -### Editing the data file +Adds a facility to your facility list. -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +Format: `addf n/NAME l/LOCATION t/TIME c/CAPACITY` + +
+ +**:information_source: Note:** You will not be able to add facilities with the same `NAME` and `LOCATION` into the list +as they are considered duplicates. +
+ +Examples: + +* `addf n/Court 1 l/University Sports Hall t/1500 c/5` adds Court 1 at University Sports Hall at 3pm with a capacity of + 5 +* `addf n/Court 19 l/Tampines Hub Badminton Hall t/1700 c/5` adds Court 19 at Tampines Hub Badminton Hall at 5pm with a capacity of 5 :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + +#### Listing all facilities: `listf` + +Shows a list of all your facilities. + +Format: `listf` + +[Back to Table of Contents](#table-of-contents) + +#### Deleting a facility: `deletef` + +Removes a facility from your facility list. + +Format: `deletef FACILITY_INDEX` + +* Deletes the facility at the specified `FACILITY_INDEX` +* `FACILITY_INDEX` refers to the index number shown in the currently displayed facility list +* `FACILITY_INDEX` **must be a positive integer** 1, 2, 3… + +Examples: + +* `listf` followed by `deletef 2` deletes the second facility in the facility list +* `findf Court 1` followed by `deletef 1` deletes the first facility in the results of the `findf` command + +[Back to Table of Contents](#table-of-contents) + +#### Editing a facility: `editf` + +Edits an existing facility in your facility list. + +Format: `editf FACILITY_INDEX [n/NAME] [l/LOCATION] [t/TIME] [c/CAPACITY]` + +* Edits the facility at the specified `FACILITY_INDEX` +* `FACILITY_INDEX` refers to the index number shown in the displayed facility list +* `FACILITY_INDEX` **must be a positive integer** 1, 2, 3… +* At least one of the optional fields must be provided +* Existing values will be updated to the input values + +
:exclamation: **Caution:** +All members allocated to the facility will be unallocated if you edit its `CAPACITY` to be below the number of allocated +members. This action is **irreversible**. +
+ +
+ +**:information_source: Note:** You will not be able to edit the facility if it has the same `NAME` and `LOCATION` as +another existing facility as they are considered duplicates. +
+ +Examples: + +* `editf 1 n/Court 5` edits the name of the first facility to be `Court 5` +* `editf 2 n/Court 20 l/University Sports Hall` edits the name and location of the second facility to be `Court 20` + and `University Sports Hall` respectively + +[Back to Table of Contents](#table-of-contents) + +#### Finding a facility: `findf` + +If you are looking for specific facilities, `findf` lets you find and filter facilities whose location contains +any of the given keywords. + +Format: `findf KEYWORD [MORE_KEYWORDS]...` + +* `KEYWORD` and `MORE_KEYWORDS` are **case-insensitive**. e.g. `Utown` will match `utown` +* Only the location is searched +* Only **full words** will be matched e.g. `Utown` will not match `town` +* Facilities matching **at least one** keyword will be returned (i.e. OR search) e.g `Utown Redhill` will + return `Utown Field` and `Redhill Sports Complex` + +Examples: + +* `findf redhill` returns `Redhill Sports Complex` and `Redhill Field` +* `findf opp tampines` returns `Opp University Hall` and `Tampines Hub Badminton Hall` :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + +#### Splitting members into facilities: `split` + +Organise your next training session seamlessly by splitting members into the facilities based on its capacity and +members' availability using `split`. + +Format: `split DAY` + +* Allocate members available on the specified `DAY` to each facility +* `DAY` **must be a positive integer from 1 to 7**, whereby 1 represents Monday and 7 represents Sunday +* All facilities are assumed to be available and have the same capacity on every day of the week + +
+:exclamation: **Caution:** +Executing this command will overwrite all existing allocations you have made for the given day. This action is **irreversible**. +You can use the `export` command +(see [Exporting facility details and member allocation](#exporting-facility-details-and-member-allocations-export)) +to save your previous allocations before using the `split` command again. +
+ +
+ +**:information_source: Note:** SportsPA will warn you when there are no members available on the specified day or insufficient facilities +to accommodate all available members and the allocation will not be executed. + +
+ +Examples: +* `split 1` splits members into groups for training on Monday of that week and displays the list of allocations to the user :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + + +#### Deallocating a member from a facility: `deallocate` + +You can easily accommodate changes in member's availability by deallocating a member from a facility they were +previously allocated to on a specified day, using `deallocate`. + +Format: `deallocate MEMBER_INDEX FACILITY_INDEX DAY` + +* `MEMBER_INDEX` refers to the index shown in the currently displayed member list +* `FACILITY_INDEX` refers to the index shown in the currently displayed facility list +* Both `MEMBER_INDEX` and `FACILITY_INDEX` **must be positive integers** 1, 2, 3… +* `DAY` **must be a positive integer from 1 to 7**, whereby 1 represents Monday and 7 represents Sunday + +Examples: +* `deallocate 2 4 5` removes the first member in the displayed member list from the allocation list of the fourth facility in the displayed facility list on Friday + +[Back to Table of Contents](#table-of-contents) + +#### Allocating a member to a facility: `allocate` + +If the allocations of the `split` command are undesirable, you can manually allocate a member to a facility +using `allocate`. + +Format: `allocate MEMBER_INDEX FACILITY_INDEX DAY` + +* `MEMBER_INDEX` refers to the index shown in the currently displayed member list +* `FACILITY_INDEX` refers to the index shown in the currently displayed facility list +* Both `MEMBER_INDEX` and `FACILITY_INDEX` **must be positive integers** 1, 2, 3… +* `DAY` **must be a positive integer from 1 to 7**, whereby 1 represents Monday and 7 represents Sunday + +
+ +**:information_source: Note:** +* Members you choose to allocate must be available on the specified day and cannot be allocated to facilities which are at maximum capacity. +* You may allocate the same member to multiple facilities on the same day but please ensure that the timings are reasonable + as SportsPa currently does not perform this check! +
+ +Examples: +* `allocate 1 1 5` adds the first member in the displayed member list to the allocation list
of the first facility in the displayed facility list on Friday :camera: + +

+ +

+ +[Back to Table of Contents](#table-of-contents) + +#### Exporting facility details and member allocations: `export` + +Export all facilities’ details and their allocation list to a CSV (comma-separated values) file so that +you can share them with your members. + +Format: `export` + +* The exported CSV file will be created in `[JAR file location]/data/exportedData.csv` +* The CSV file will contain 5 headers: Facility Name, Location, Time, Capacity and Member Allocation + +
+ +**:information_source: Note:** If exportedData.csv already exists before this command is executed, +the existing CSV file will be overwritten. So, make sure to change the file name of the existing +CSV file before running the export command if you want to save it. +
+ +#### Clearing all entries in facility list: `clearf` + +Clears all facilities from your facility list. + +Format: `clearf`
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. +All facility data will be removed immediately after this command is executed. This action is **irreversible**. +
+ +[Back to Table of Contents](#table-of-contents) + +### General Features + +#### Getting help: `help` + +Shows a message explaining how you can access our help page. + +Format: `help` + +[Back to Table of Contents](#table-of-contents) + +#### Creating an alias: `alias` + +Some of our commands' names may be not be to your liking. Hence, SportsPA offers you the flexibility of personalising +the commands you use. With `alias`, you can create a shortcut name for any command. + +Format: `alias s/SHORTCUT cw/COMMAND_WORD` + +* Creates an alias that allows the specified `COMMAND_WORD` to be executed with the specified `SHORTCUT` +* `SHORTCUT` **must be one word and not an existing command** +* `COMMAND_WORD` **must be an existing command** + +
+ +**:information_source: Note:** If you create an alias whose given `SHORTCUT` already exists, the newly created alias +will replace that existing one.
-### Archiving data files `[coming in v2.0]` +Examples: + +* `alias s/lf cw/listf` creates an alias for `listf` command. Entering `lf` will execute the `listf` command and a list + of all facilities will be shown :camera: + +![aliasExample](images/aliasExample.png) -_Details coming soon ..._ +[Back to Table of Contents](#table-of-contents) + +#### Listing all aliases: `aliases` + +Shows a list of your created aliases in the Result Window. + +Format: `aliases` + +[Back to Table of Contents](#table-of-contents) + +#### Deleting an alias: `unalias` + +Deletes an alias you have created. + +Format: `unalias SHORTCUT` + +* Deletes the alias associated with the specified `SHORTCUT` + +Examples: + +* `unalias lf` deletes the alias with shortcut name `lf` + +[Back to Table of Contents](#table-of-contents) + +#### Saving the data + +Your SportsPA data is saved in the hard disk automatically after any command that changes the data. They are saved as a +JSON file `[JAR file location]/data/sportspa.json`. Though **not recommended**, if you are familiar with JSON, you can +directly change the contents, *e.g a member's name*, in the data file, which will be reflected in SportsPA. + +
:exclamation: **Caution:** +If changes made to the data file makes its format invalid, SportsPA will start with an empty data file at the next run. +
+ +[Back to Table of Contents](#table-of-contents) + +#### Exiting the program: `exit` + +Exits the program. + +Format: `exit` + +[Back to Table of Contents](#table-of-contents) -------------------------------------------------------------------------------------------------------------------- ## FAQ -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**Q**: How do I check if Java 11 is installed on my Computer?
+**A**: Open your Operating System's command prompt and enter `java -version`. Java 11 is installed if the output shows Java +11.

+**Q**: How do I install Java 11 on my Computer?
+**A**: Visit [this site](https://docs.oracle.com/en/java/javase/11/install/overview-jdk-installation.html#GUID-8677A77F-231A-40F7-98B9-1FD0B48C346A) +and follow the instructions specific to your operating system. You may have to download and run an installer to install Java 11 on your computer.

+**Q**: How do I transfer my data in SportsPA to another Computer?
+**A**: +1. On your current computer, navigate to `[JAR file location]/data/sportspa.json`. Make a copy of the `sportspa.json` file and transfer it to your other computer. +2. On the other computer, navigate to +`[JAR file location]/data/sportspa.json` and replace the empty data file it creates with the data file you have just copied. + +[Back to Table of Contents](#table-of-contents) -------------------------------------------------------------------------------------------------------------------- ## Command summary +### Member-Specific Commands + Action | Format, Examples --------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +**Add member**| `addm n/NAME p/PHONE_NUMBER [d/DAY(S)] [t/TAG]...`
e.g. `addm n/John Doe p/91111111`, `addm n/John Doe p/91111111 d/1 3 5`, `addm n/John Doe p/91111111 d/1 3 5 t/exco` +**List members**| `listm` +**Delete member**| `deletem MEMBER_INDEX`
e.g. `deletem 1` +**Edit member**| `editm MEMBER_INDEX [n/NAME] [p/PHONE_NUMBER] [t/TAG]...`
e.g. `editm 1 n/Jonathan p/93837283` +**Find member**| `findm [n/NAME] [p/PHONE] [d/DAY(S)] [tda/TODAY_ATTENDANCE] [tta/TOTAL_ATTENDANCE] [t/TAG]...`
e.g. `findm n/John`, `findm t/exco t/y2` +**Sort members**| `sortm by/SORT_ORDER`
e.g. `sortm by/name`, `sortm by/tag` +**Set member availability**| `setm MEMBER_INDEX [MORE_INDICES]... d/DAY(S)`
e.g.`setm 1 2 3 d/2 3 5` +**Mark member attendance**| `mark MEMBER_INDEX [MORE_INDICES]...`
e.g. `mark 1 2` +**Unmark member attendance**| `unmark MEMBER_INDEX [MORE_INDICES]...`
e.g. `unmark 1 2` +**Clear membber attendance**| `cleara` +**Import multiple members**| `import CSV_FILE_PATH`
e.g.`import myFile.csv` +**Clear members**| `clearm` + +[Back to Table of Contents](#table-of-contents) + +### Facility-Specific Commands + +Action | Format, Examples +--------|------------------ +**Add facility**| `addf n/NAME l/LOCATION t/TIME c/CAPACITY`
e.g. `addf n/Court 1 l/University Sports Hall t/1500 c/5` +**List facilities**| `listf` +**Delete facility**| `deletef FACILITY_INDEX`
e.g. `deletef 4` +**Edit facility**| `editf FACILITY_INDEX [n/NAME] [l/LOCATION] [t/TIME] [c/CAPACITY]`
e.g. `editf 2 n/Court 20 l/University Sports Hall` +**Find facility**| `findf KEYWORD [MORE_KEYWORDS]...`
e.g. `findf Clementi`, `findf Utown` +**Split members into facilities**| `split DAY`
e.g. `split 1` +**Deallocate member from a facility**|`deallocate MEMBER_INDEX FACILITY_INDEX DAY`
e.g. `deallocate 2 3 4` +**Allocate member to a facility**|`allocate MEMBER_INDEX FACILITY_INDEX DAY`
e.g. `allocate 1 2 5` +**Export facility details and
member allocation**| `export` +**Clear facilities**|`clearf` + +[Back to Table of Contents](#table-of-contents) + +### General Commands + +Action | Format, Examples +--------|------------------ +**Help**| `help` +**Creates alias**| `alias s/SHORTCUT cw/COMMAND_WORD`
e.g. `alias s/lf cw/listf` +**List aliases**| `aliases` +**Deletes alias**| `unalias SHORTCUT`
e.g. `unalias lf` +**Exit**| `exit` + +[Back to Table of Contents](#table-of-contents) diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..4cc1dd0fc74 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "SportsPA" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S1-CS2103T-W12-1/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..3eee1745c8a 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "SportsPA"; font-size: 32px; } } diff --git a/docs/diagrams/AliasActivityDiagram.puml b/docs/diagrams/AliasActivityDiagram.puml new file mode 100644 index 00000000000..41eaae785ac --- /dev/null +++ b/docs/diagrams/AliasActivityDiagram.puml @@ -0,0 +1,21 @@ +@startuml +start +:User enters command; +:Parse command word; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([command word is an alias]) + :Replace shortcut with + actual command word; +else ([else]) +endif +:Parse command arguments; +if () then ([command is valid]) + :Execute command; +else ([else]) +: Throw ParseException; +endif +stop +@enduml diff --git a/docs/diagrams/AliasClassDiagram.puml b/docs/diagrams/AliasClassDiagram.puml new file mode 100644 index 00000000000..553779dbe86 --- /dev/null +++ b/docs/diagrams/AliasClassDiagram.puml @@ -0,0 +1,19 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR + +Package AliasMap <>{ +Class AliasMap +Class Alias +Class Shortcut +Class CommandWord +} + +AliasMap -right-> "*" Alias +Alias -> " 1" Shortcut +Alias -> "1" CommandWord + +Shortcut -[hidden]up- CommandWord +@enduml diff --git a/docs/diagrams/AliasSequenceDiagram.puml b/docs/diagrams/AliasSequenceDiagram.puml new file mode 100644 index 00000000000..acb22248c79 --- /dev/null +++ b/docs/diagrams/AliasSequenceDiagram.puml @@ -0,0 +1,60 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant ":ListMembersCommand" as ListMembersCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":AliasMap" as AliasMap MODEL_COLOR +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("l") +activate LogicManager + +LogicManager -> SportsPaParser : parseCommand("l") +activate SportsPaParser + +SportsPaParser -> AliasMap : convertAliasIfPresent() +activate AliasMap +AliasMap --> SportsPaParser : "listm" +deactivate AliasMap + +create ListMembersCommand +SportsPaParser -> ListMembersCommand +activate ListMembersCommand + +ListMembersCommand --> SportsPaParser +deactivate ListMembersCommand + +SportsPaParser --> LogicManager +deactivate SportsPaParser + +LogicManager -> ListMembersCommand : execute() +activate ListMembersCommand + +ListMembersCommand -> Model : updateFilteredMemberList(SHOW_ALL) +activate Model + +Model --> ListMembersCommand +deactivate Model + +create CommandResult +ListMembersCommand -> CommandResult +activate CommandResult + +CommandResult --> ListMembersCommand +deactivate CommandResult + +ListMembersCommand --> LogicManager : result +deactivate ListMembersCommand +ListMembersCommand -[hidden]-> LogicManager : result +destroy ListMembersCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AliasState0.puml b/docs/diagrams/AliasState0.puml new file mode 100644 index 00000000000..e180dcbb4e0 --- /dev/null +++ b/docs/diagrams/AliasState0.puml @@ -0,0 +1,10 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Initial State + +class State3 as "__:AliasMap__" + +@end diff --git a/docs/diagrams/AliasState1.puml b/docs/diagrams/AliasState1.puml new file mode 100644 index 00000000000..115622d1cb5 --- /dev/null +++ b/docs/diagrams/AliasState1.puml @@ -0,0 +1,27 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command "alias s/l cw/listf" + +object "__:Shortcut__" as State1 { +shortcut = "l" +} + +object "__:CommandWord__" as State2 { +command = "listf" +} + +class State3 as "__:AliasMap__" +class State4 as "__:Alias__" + + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 +State3 -[hidden]up-> State4 + +State4 -up-> State2 +State4 -up-> State1 +State3 --> State4 +@end diff --git a/docs/diagrams/AliasState2.puml b/docs/diagrams/AliasState2.puml new file mode 100644 index 00000000000..299e0a92b6e --- /dev/null +++ b/docs/diagrams/AliasState2.puml @@ -0,0 +1,30 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command "alias s/l cw/listm" + +object "__:Shortcut__" as State1 { +shortcut = "l" +} + +object "__:CommandWord__" as State2 { +command = "listm" +} + +note "CommandWord replaced" as N1 + +class State3 as "__:AliasMap__" +class State4 as "__:Alias__" + + +State1 -[hidden]right-> State2 +State2 -[hidden]right-> State3 +State3 -[hidden]up-> State4 + +State4 -up-> State2 +State4 -up-> State1 +State3 --> State4 + +@end diff --git a/docs/diagrams/AliasState3.puml b/docs/diagrams/AliasState3.puml new file mode 100644 index 00000000000..44149e06680 --- /dev/null +++ b/docs/diagrams/AliasState3.puml @@ -0,0 +1,10 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title After command unalias `l` + +class State3 as "__:AliasMap__" + +@end diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..0b56400d42e 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,19 +7,20 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "deletem 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("deletem 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) + +logic -[LOGIC_COLOR]> model : deleteMember(m) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveSportsPa(sportsPa) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..113583ab266 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,19 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +SportsPa *-right-> "1" UniqueMemberList +SportsPa *-right-> "1" UniqueTagList +UniqueTagList -[hidden]down- UniqueMemberList +UniqueTagList -[hidden]down- UniqueMemberList -UniqueTagList *-right-> "*" Tag -UniquePersonList -right-> Person +UniqueTagList *-right-> " *" Tag +UniqueMemberList -right-> "~*all" Member -Person -up-> "*" Tag +Member -up-> " *" Tag -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address +Member *--> "1" Name +Member *--> "1" Phone +Member *--> "1" Availability +Member *--> "1" TodayAttendance +Member *--> "1" TotalAttendance @enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..2478e80cb13 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -5,10 +5,10 @@ start 'Since the beta syntax does not support placing the condition outside the 'diamond we place it as the true branch instead. -if () then ([command commits AddressBook]) +if () then ([command commits SportsPA]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save SportsPA to + sportsPaStateList; else ([else]) endif stop diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..5ab50010c45 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,9 +3,9 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant ":DeleteMemberCommandParser" as DeleteCommandParser LOGIC_COLOR +participant "d:DeleteMemberCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,20 +13,20 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("deletem 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> SportsPaParser : parseCommand("deletem 1") +activate SportsPaParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +SportsPaParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> SportsPaParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +SportsPaParser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -36,19 +36,19 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> SportsPaParser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> SportsPaParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +SportsPaParser --> LogicManager : d +deactivate SportsPaParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deleteMember(m) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/FindMemberActivityDiagram.puml b/docs/diagrams/FindMemberActivityDiagram.puml new file mode 100644 index 00000000000..c24558d5112 --- /dev/null +++ b/docs/diagrams/FindMemberActivityDiagram.puml @@ -0,0 +1,26 @@ +@startuml +!include style.puml +start +:User enters find member command; +:Parse command word; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([command word is an alias]) + :Replace shortcut with + actual command word; +else ([else]) +endif +:Parse command arguments; +if () then ([arguments are valid]) +: Generate predicate based on +the attribute keywords specified; + :Execute find member command; +:Update member list; +:Display member list; +stop +else ([else]) +:Throw ParseException; +stop +@enduml diff --git a/docs/diagrams/FindMemberSequenceDiagram.puml b/docs/diagrams/FindMemberSequenceDiagram.puml new file mode 100644 index 00000000000..82f26a9f6ee --- /dev/null +++ b/docs/diagrams/FindMemberSequenceDiagram.puml @@ -0,0 +1,91 @@ +@startuml +!include style.puml +skinparam ParticipantFontSize 20 +skinparam ArrowFontSize 20 + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant ":FindMemberCommandParser" as FindMemberCommandParser LOGIC_COLOR +participant "f:FindMemberCommand" as FindMemberCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "p:MemberMatchesKeywordsPredicate" as Predicate MODEL_COLOR +participant ":Model" as Model MODEL_COLOR +end box + +box ObservableList UI_COLOR_T2 +participant ":FilteredMemberList" as FilteredList UI_COLOR_T3 +endbox + +[-> LogicManager : execute("findm t/exco) +activate LogicManager + +LogicManager -> SportsPaParser : parseCommand("findm t/exco") +activate SportsPaParser + +create FindMemberCommandParser +SportsPaParser -> FindMemberCommandParser +activate FindMemberCommandParser + +FindMemberCommandParser --> SportsPaParser +deactivate FindMemberCommandParser + +SportsPaParser -> FindMemberCommandParser : parse("t/exco") +activate FindMemberCommandParser + +create Predicate +FindMemberCommandParser -> Predicate +activate Predicate + +Predicate --> FindMemberCommandParser : p +deactivate Predicate + +create FindMemberCommand +FindMemberCommandParser -> FindMemberCommand : FindMemberCommand(p) + +activate FindMemberCommand +FindMemberCommand --> FindMemberCommandParser : f +deactivate FindMemberCommand + +FindMemberCommandParser --> SportsPaParser : f +deactivate FindMemberCommandParser +FindMemberCommandParser -[hidden]-> SportsPaParser : f +destroy FindMemberCommandParser + +SportsPaParser --> LogicManager : f +deactivate SportsPaParser + +LogicManager -> FindMemberCommand : execute(model) +activate FindMemberCommand + +FindMemberCommand -> Model : updateFilteredMemberList(p) +activate Model +Model -> FilteredList : setPredicate(p) + +activate FilteredList +FilteredList --> Model +deactivate FilteredList + +Model --> FindMemberCommand +deactivate Model + +create CommandResult +FindMemberCommand -> CommandResult + +activate CommandResult +CommandResult --> FindMemberCommand +deactivate CommandResult + +FindMemberCommand --> LogicManager : result +deactivate FindMemberCommand +FindMemberCommand -[hidden]-> LogicManager +destroy FindMemberCommand + +destroy Predicate + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ImportActivityDiagram.puml b/docs/diagrams/ImportActivityDiagram.puml new file mode 100644 index 00000000000..0928c6ab74b --- /dev/null +++ b/docs/diagrams/ImportActivityDiagram.puml @@ -0,0 +1,23 @@ +@startuml + +start +:User enters import command; +:Parse import command; +:ImportCommand.execute(); +:ImportCommand.parseCsv(); +:Iterate through imported member list; +repeat + if () then (isValidImport) + if () then (hasMember) + :Update existing member details + using data from CSV file; + else (else) + :Add imported member; + endif + else (else) + endif +repeat while () is (else) +-> All imported members iterated; +stop + +@enduml diff --git a/docs/diagrams/ImportSequenceDiagram.puml b/docs/diagrams/ImportSequenceDiagram.puml new file mode 100644 index 00000000000..7d8a4bf7e4b --- /dev/null +++ b/docs/diagrams/ImportSequenceDiagram.puml @@ -0,0 +1,87 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant ":ImportCommandParser" as ImportCommandParser LOGIC_COLOR +participant "i:ImportCommand" as ImportCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("import myFile.csv") +activate LogicManager + +LogicManager -> SportsPaParser : parseCommand("import myFile.csv") +activate SportsPaParser + +create ImportCommandParser +SportsPaParser -> ImportCommandParser +activate ImportCommandParser + +ImportCommandParser --> SportsPaParser +deactivate ImportCommandParser + +SportsPaParser -> ImportCommandParser : parse("import myFile.csv") +activate ImportCommandParser + +create ImportCommand +ImportCommandParser -> ImportCommand +activate ImportCommand + +ImportCommand --> ImportCommandParser : i +deactivate ImportCommand + +ImportCommandParser --> SportsPaParser : i +deactivate ImportCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +ImportCommandParser -[hidden]-> SportsPaParser +destroy ImportCommandParser + +SportsPaParser --> LogicManager : i +deactivate SportsPaParser + +LogicManager -> ImportCommand : execute() +activate ImportCommand + +loop until all imported members have been iterated + + ImportCommand -> Model : isValidImport(m) + activate Model + + Model --> ImportCommand + deactivate Model + + ImportCommand -> Model : hasMember(m) + activate Model + + Model --> ImportCommand + deactivate Model + + ImportCommand -> Model : setMember(p) + activate Model + + Model --> ImportCommand + deactivate Model + +end + +create CommandResult +ImportCommand -> CommandResult +activate CommandResult + +CommandResult --> ImportCommand +deactivate CommandResult + +ImportCommand --> LogicManager : result +deactivate ImportCommand + +[<--LogicManager +deactivate LogicManager + +destroy ImportCommand +@enduml diff --git a/docs/diagrams/ImportStep1ObjectDiagram.puml b/docs/diagrams/ImportStep1ObjectDiagram.puml new file mode 100644 index 00000000000..76659affd6c --- /dev/null +++ b/docs/diagrams/ImportStep1ObjectDiagram.puml @@ -0,0 +1,20 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Initial state + +object "__:ModelManager__" as State1 + +object "__:SportsPa__" as State2 + +object "__:Member__" as State3 { +Name:Bob +Phone:12345678 +} + +State1 --> State2 +State2 -right-> State3 +State1 --> State3 +@enduml diff --git a/docs/diagrams/ImportStep3ObjectDiagram.puml b/docs/diagrams/ImportStep3ObjectDiagram.puml new file mode 100644 index 00000000000..8142ed5d84f --- /dev/null +++ b/docs/diagrams/ImportStep3ObjectDiagram.puml @@ -0,0 +1,28 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Final state + +object "__:ModelManager__" as State1 + +object "__:SportsPa__" as State2 + +object "__:Member__" as State3 { +Name:Bob +Phone:97848343 +} + +object "__:Member__" as State4 { +Name:Amy +Phone:94379855 +} + +State1 --> State2 +State1 --> State3 +State1 --> State4 + +State2 --> State3 +State2 --> State4 +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 6d14b17b361..e07a9a7f57c 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class SportsPaParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" SportsPaParser +SportsPaParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > @@ -38,7 +38,7 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = AddMemberCommand, \nAddFacilityCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/MarkActivityDiagram.puml b/docs/diagrams/MarkActivityDiagram.puml new file mode 100644 index 00000000000..7683d95445d --- /dev/null +++ b/docs/diagrams/MarkActivityDiagram.puml @@ -0,0 +1,25 @@ +@startuml +!include style.puml + +start +:User enters mark command; +:Parse command word; + +if () then ([command word is an alias]) + :Replace shortcut with + actual command word; +else ([else]) +endif +:Parse command arguments; +:Convert string of numbers to list of Index; + +if () then ([indices are valid]) + :Mark attendance of members at indices; +else ([else]) + : Throw CommandException; +endif + + +stop + +@enduml diff --git a/docs/diagrams/MarkObjectDiagram.puml b/docs/diagrams/MarkObjectDiagram.puml new file mode 100644 index 00000000000..28f11629bef --- /dev/null +++ b/docs/diagrams/MarkObjectDiagram.puml @@ -0,0 +1,23 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Initial state + +Object "__:ModelManager__" as State1 { +} + +object "__:Member__" as State2 { +todayAttendance = false +totalAttendance = 0 +} + +object "__:Member__" as State3 { +todayAttendance = false +totalAttendance = 0 +} + +State1 --> State2 +State1 --> State3 +@enduml diff --git a/docs/diagrams/MarkObjectDiagramModified.puml b/docs/diagrams/MarkObjectDiagramModified.puml new file mode 100644 index 00000000000..3d02a0cf89c --- /dev/null +++ b/docs/diagrams/MarkObjectDiagramModified.puml @@ -0,0 +1,23 @@ +@startuml +!include style.puml +skinparam ClassFontColor #000000 +skinparam ClassBorderColor #000000 + +title Final state + +Object "__:ModelManager__" as State1 { +} + +object "__:Member__" as State2 { +todayAttendance = true +totalAttendance = 1 +} + +object "__:Member__" as State3 { +todayAttendance = true +totalAttendance = 1 +} + +State1 --> State2 +State1 --> State3 +@enduml diff --git a/docs/diagrams/MarkSequenceDiagram.puml b/docs/diagrams/MarkSequenceDiagram.puml new file mode 100644 index 00000000000..071829fbaa3 --- /dev/null +++ b/docs/diagrams/MarkSequenceDiagram.puml @@ -0,0 +1,59 @@ +@startuml +!include style.puml +skinparam ArrowFontSize 14 + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant "m: MarkCommand" as MarkCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":Member" as Member MODEL_COLOR +end box + +[-> LogicManager : execute(mark 1 2) +activate LogicManager + +LogicManager -> SportsPaParser : parseCommand(mark 1 2) +activate SportsPaParser + +create MarkCommand +SportsPaParser -> MarkCommand +activate MarkCommand + +MarkCommand --> SportsPaParser +deactivate MarkCommand + +SportsPaParser --> LogicManager : m +deactivate SportsPaParser + +LogicManager -> MarkCommand : execute() +activate MarkCommand + +MarkCommand -> Model : markMembersAttendance(1, 2) +activate Model + +loop until all members marked +Model -> Model: markOneMemberAttendance() +activate Model +Model -> Member : setPresent() +activate Member +Member --> Model +deactivate Member +return +end + +Model --> MarkCommand +deactivate Model + +MarkCommand --> LogicManager : result +deactivate MarkCommand +MarkCommand -[hidden]-> LogicManager : result +destroy MarkCommand + +[<-- LogicManager +deactivate LogicManager + +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 1122257bd9a..7f8568ed18d 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,50 +5,71 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> +Interface ReadOnlySportsPa <> Interface ReadOnlyUserPrefs <> Interface Model <> -Class AddressBook -Class ReadOnlyAddressBook +Class SportsPa +Class ReadOnlySportsPa Class Model Class ModelManager Class UserPrefs Class ReadOnlyUserPrefs - -Class UniquePersonList -Class Person -Class Address -Class Email +Class UniqueMemberList +Class Member Class Name Class Phone +Class Availability Class Tag +Class TodayAttendance +Class TotalAttendance + +Class UniqueFacilityList +Class Facility +Class AllocationMap +Class FacilityName +Class Capacity +Class Location +Class Time + } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +SportsPa .up.|> ReadOnlySportsPa ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlySportsPa +ModelManager -left-> "1" SportsPa ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +SportsPa *--> "1" UniqueMemberList +UniqueMemberList --> "~* all" Member +Member *--> "1" Name +Member *--> "1" Phone +Member *--> "1" Availability +Member *--> "*" Tag +Member *--> "1" TodayAttendance +Member *--> "1" TotalAttendance + +SportsPa *--> "1" UniqueFacilityList +UniqueFacilityList --> "~*all " Facility +Facility *--> "1" FacilityName +Facility *--> "1" Capacity +Facility *--> "1" Location +Facility *--> "1" Time +Facility *--> "1" AllocationMap +AllocationMap --> "*" Member Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +Availability -[hidden]right-> Phone +FacilityName -[hidden]left- AllocationMap +UniqueMemberList -[hidden]right- UniqueFacilityList -ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Member +ModelManager -->" ~* filtered" Facility @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 6ba585cba01..281c162ca13 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,22 +9,23 @@ Class XYZCommand package "Parser classes"{ Interface Parser <> -Class AddressBookParser +Class SportsPaParser Class XYZCommandParser Class CliSyntax Class ParserUtil Class ArgumentMultimap Class ArgumentTokenizer Class Prefix +note left of XYZCommandParser: XYZCommandParser = \nAddMemberCommandParser, \nFindMemberCommandParser, etc } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> SportsPaParser -AddressBookParser .down.> XYZCommandParser: creates > +SportsPaParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +SportsPaParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/SplitActivityDiagram.puml b/docs/diagrams/SplitActivityDiagram.puml new file mode 100644 index 00000000000..951dfe06a0b --- /dev/null +++ b/docs/diagrams/SplitActivityDiagram.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml +start +:User enters split command; +:Parse command word; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([command word is an alias]) + :Replace shortcut with + actual command word; +else ([else]) +endif +:Parse command arguments; + +if () then ([arguments are valid]) + : Execute split command; + +if () then ([Member list or +Facility list is empty]) + : Throw CommandException; +stop +else ([else]) + :Split is attempted; + if () then ([Insufficient capacity or no members available]) + :Throw CommandException; + stop + else ([else]) + : All members allocated to facilities; + endif +endif + +:Allocation maps of facilities are updated; + +stop +else ([else]) +:Throw ParseException; +stop +@enduml diff --git a/docs/diagrams/SplitSequenceDiagram.puml b/docs/diagrams/SplitSequenceDiagram.puml new file mode 100644 index 00000000000..51ce37e033c --- /dev/null +++ b/docs/diagrams/SplitSequenceDiagram.puml @@ -0,0 +1,102 @@ +@startuml +!include style.puml +skinparam ParticipantFontSize 20 +skinparam ArrowFontSize 20 + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR +participant ":SplitCommandParser" as SplitCommandParser LOGIC_COLOR +participant "s:SplitCommand" as SplitCommand LOGIC_COLOR +participant "r:CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant "p:MemberAvailableOnDayPredicate" as Predicate MODEL_COLOR +participant ":Model" as Model MODEL_COLOR +participant ":SportsPa" as SportsPa MODEL_COLOR +participant ":UniqueFacilityList" as Facilities MODEL_COLOR +end box + +box ObservableList UI_COLOR_T2 +participant "f:FilteredMemberList" as FilteredList UI_COLOR_T3 +endbox + +[-> LogicManager : execute("split 1") +activate LogicManager + +LogicManager -> SportsPaParser : parseCommand("split 1") +activate SportsPaParser + +create SplitCommandParser +SportsPaParser -> SplitCommandParser +activate SplitCommandParser + +SplitCommandParser --> SportsPaParser +deactivate SplitCommandParser + +SportsPaParser -> SplitCommandParser : parse("1") +activate SplitCommandParser + +create SplitCommand +SplitCommandParser -> SplitCommand : SplitCommand(1) + +activate SplitCommand +SplitCommand --> SplitCommandParser : s +deactivate SplitCommand + +SplitCommandParser --> SportsPaParser : s +deactivate SplitCommandParser +SplitCommandParser -[hidden]-> SportsPaParser : f +destroy SplitCommandParser + +SportsPaParser --> LogicManager : s +deactivate SportsPaParser + +LogicManager -> SplitCommand : execute(model) +activate SplitCommand + +create Predicate +SplitCommand -> Predicate : MemberAvailableOnDayPredicate(1) +activate Predicate +Predicate --> SplitCommand : p +deactivate Predicate +SplitCommandParser -[hidden]-> SportsPaParser : f +destroy Predicate + +SplitCommand -> Model : split(p, 1) + +activate Model +Model -> FilteredList : setPredicate(p) +activate FilteredList +FilteredList --> Model +deactivate FilteredList +Model -> SportsPa : split(f, 1) + +activate SportsPa +SportsPa -> Facilities : allocateMembersToFacilities(f, 1) + +activate Facilities +Facilities --> SportsPa +deactivate Facilities + +SportsPa --> Model +deactivate SportsPa + +Model --> SplitCommand +deactivate Model + +create CommandResult +SplitCommand -> CommandResult +activate CommandResult +CommandResult --> SplitCommand +deactivate CommandResult + +SplitCommand --> LogicManager : r +deactivate SplitCommand +SplitCommandParser -[hidden]-> SportsPaParser : f +destroy SplitCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 85ac3ea2dee..c0d2cd0e140 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,11 +14,13 @@ Class JsonUserPrefsStorage Interface Storage <> Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Interface AddressBookStorage <> -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +package "SportsPA Storage" #F4F6F6{ +Interface SportsPaStorage <> +Class SportsPaStorage +Class JsonSportsPaStorage +Class JsonSerializableSportsPa +Class JsonAdaptedMember +Class JsonAdaptedFacility Class JsonAdaptedTag } @@ -29,15 +31,17 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" SportsPaStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> SportsPaStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonSportsPaStorage .up.|> SportsPaStorage +JsonSportsPaStorage ..> JsonSerializableSportsPa +JsonSerializableSportsPa --> "*" JsonAdaptedMember +JsonSerializableSportsPa -> "*" JsonAdaptedFacility +JsonAdaptedFacility --> "*" JsonAdaptedMember +JsonAdaptedMember --> "*" JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index ecae4876432..786c9b35ff9 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,10 +11,12 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class MemberListPanel +Class MemberCard Class StatusBarFooter Class CommandBox +Class FacilityListPanel +Class FacilityCard } package Model <> { @@ -32,29 +34,37 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" MemberListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow *-down-> "1" FacilityListPanel -PersonListPanel -down-> "*" PersonCard +MemberListPanel -down-> "*" MemberCard +FacilityListPanel -down-> "*" FacilityCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +MemberListPanel -right-|> UiPart +MemberCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +FacilityCard --|> UiPart +FacilityListPanel -left-|> UiPart -PersonCard ..> Model +MemberCard ..> Model +FacilityCard ..> Model +UiPart .[hidden]left. Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +MemberListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter +FacilityCard -[hidden]left- MemberCard +FacilityListPanel -[hidden]left- MemberListPanel MainWindow -[hidden]-|> UiPart @enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..c62ad726e0a 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,15 +6,16 @@ skinparam ClassBorderColor #000000 title Initial state package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp2:SportsPa__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 hide State2 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State1 @end +@enduml diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..d90ad7f5664 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -6,9 +6,9 @@ skinparam ClassBorderColor #000000 title After command "delete 5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp2:SportsPa__" } State1 -[hidden]right-> State2 @@ -16,7 +16,7 @@ State2 -[hidden]right-> State3 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..f60f6300fc7 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "add n/David" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp2:SportsPa__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 @end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..50f7e540713 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "undo" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp2:SportsPa__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..5a78ad5a324 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "list" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp2:SportsPa__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..c9fd674b672 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "clear" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" + class State1 as "__sp0:SportsPa__" + class State2 as "__sp1:SportsPa__" + class State3 as "__sp3:SportsPa__" } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" #FFFFFF Pointer -up-> State3 note right on link: State ab2 deleted. diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..0bfe552aa6d 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,42 +3,42 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":SportsPaParser" as SportsPaParser LOGIC_COLOR participant "u:UndoCommand" as UndoCommand LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR +participant ":VersionedSportsPa" as VersionedSportsPa MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> SportsPaParser : parseCommand(undo) +activate SportsPaParser create UndoCommand -AddressBookParser -> UndoCommand +SportsPaParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> SportsPaParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +SportsPaParser --> LogicManager : u +deactivate SportsPaParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoSportsPa() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedSportsPa : undo() +activate VersionedSportsPa -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedSportsPa -> VersionedSportsPa :resetData(ReadOnlySportsPa) +VersionedSportsPa --> Model : +deactivate VersionedSportsPa Model --> UndoCommand deactivate Model diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index fad8b0adeaa..15af441b367 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -71,5 +71,5 @@ skinparam DefaultTextAlignment center skinparam packageStyle Rectangle hide footbox -hide members +hide empty members hide circle diff --git a/docs/images/AliasActivityDiagram.png b/docs/images/AliasActivityDiagram.png new file mode 100644 index 00000000000..a9d340a1554 Binary files /dev/null and b/docs/images/AliasActivityDiagram.png differ diff --git a/docs/images/AliasClassDiagram.png b/docs/images/AliasClassDiagram.png new file mode 100644 index 00000000000..50c507f5edd Binary files /dev/null and b/docs/images/AliasClassDiagram.png differ diff --git a/docs/images/AliasSequenceDiagram.png b/docs/images/AliasSequenceDiagram.png new file mode 100644 index 00000000000..85ea882de73 Binary files /dev/null and b/docs/images/AliasSequenceDiagram.png differ diff --git a/docs/images/AliasState0.png b/docs/images/AliasState0.png new file mode 100644 index 00000000000..6c26005dd1a Binary files /dev/null and b/docs/images/AliasState0.png differ diff --git a/docs/images/AliasState1.png b/docs/images/AliasState1.png new file mode 100644 index 00000000000..46a321ea5dd Binary files /dev/null and b/docs/images/AliasState1.png differ diff --git a/docs/images/AliasState2.png b/docs/images/AliasState2.png new file mode 100644 index 00000000000..6a8c04bd62c Binary files /dev/null and b/docs/images/AliasState2.png differ diff --git a/docs/images/AliasState3.png b/docs/images/AliasState3.png new file mode 100644 index 00000000000..75057273173 Binary files /dev/null and b/docs/images/AliasState3.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..833cbc0b335 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..6fe95a439fc 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/BetterModelClassDiagramUpdated.png b/docs/images/BetterModelClassDiagramUpdated.png new file mode 100644 index 00000000000..27f3f105b7b Binary files /dev/null and b/docs/images/BetterModelClassDiagramUpdated.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png index c08c13f5c8b..bb3bc95b470 100644 Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..43049f35899 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/FindMemberActivityDiagram.png b/docs/images/FindMemberActivityDiagram.png new file mode 100644 index 00000000000..d8ba6f4c072 Binary files /dev/null and b/docs/images/FindMemberActivityDiagram.png differ diff --git a/docs/images/FindMemberSequenceDiagram.png b/docs/images/FindMemberSequenceDiagram.png new file mode 100644 index 00000000000..05ebccf3b79 Binary files /dev/null and b/docs/images/FindMemberSequenceDiagram.png differ diff --git a/docs/images/FirstLook.png b/docs/images/FirstLook.png new file mode 100644 index 00000000000..631f1b555f3 Binary files /dev/null and b/docs/images/FirstLook.png differ diff --git a/docs/images/Gui.png b/docs/images/Gui.png new file mode 100644 index 00000000000..95a0bb3f616 Binary files /dev/null and b/docs/images/Gui.png differ diff --git a/docs/images/ImportActivityDiagram.png b/docs/images/ImportActivityDiagram.png new file mode 100644 index 00000000000..80f3ca01c8e Binary files /dev/null and b/docs/images/ImportActivityDiagram.png differ diff --git a/docs/images/ImportImplementationCsv.png b/docs/images/ImportImplementationCsv.png new file mode 100644 index 00000000000..e8d2dd337cc Binary files /dev/null and b/docs/images/ImportImplementationCsv.png differ diff --git a/docs/images/ImportSequenceDiagram.png b/docs/images/ImportSequenceDiagram.png new file mode 100644 index 00000000000..a9b2781e63d Binary files /dev/null and b/docs/images/ImportSequenceDiagram.png differ diff --git a/docs/images/ImportStep1ObjectDiagram.png b/docs/images/ImportStep1ObjectDiagram.png new file mode 100644 index 00000000000..c301299957c Binary files /dev/null and b/docs/images/ImportStep1ObjectDiagram.png differ diff --git a/docs/images/ImportStep3ObjectDiagram.png b/docs/images/ImportStep3ObjectDiagram.png new file mode 100644 index 00000000000..931fdedce00 Binary files /dev/null and b/docs/images/ImportStep3ObjectDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index c3028aa1cda..8de1fd1e278 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/MarkActivityDiagram.png b/docs/images/MarkActivityDiagram.png new file mode 100644 index 00000000000..bdb2f59568a Binary files /dev/null and b/docs/images/MarkActivityDiagram.png differ diff --git a/docs/images/MarkObjectDiagramModified_FinalState.png b/docs/images/MarkObjectDiagramModified_FinalState.png new file mode 100644 index 00000000000..6e23446c99a Binary files /dev/null and b/docs/images/MarkObjectDiagramModified_FinalState.png differ diff --git a/docs/images/MarkObjectDiagram_InitialState.png b/docs/images/MarkObjectDiagram_InitialState.png new file mode 100644 index 00000000000..cfaf8bfea02 Binary files /dev/null and b/docs/images/MarkObjectDiagram_InitialState.png differ diff --git a/docs/images/MarkSequenceDiagram.png b/docs/images/MarkSequenceDiagram.png new file mode 100644 index 00000000000..545d1a85642 Binary files /dev/null and b/docs/images/MarkSequenceDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 39d7aec4b33..90ac96b85ba 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ModelClassDiagramUpdated.png b/docs/images/ModelClassDiagramUpdated.png new file mode 100644 index 00000000000..144e361bc84 Binary files /dev/null and b/docs/images/ModelClassDiagramUpdated.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index 58ad22ce16a..801d996829c 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/SplitActivityDiagram.png b/docs/images/SplitActivityDiagram.png new file mode 100644 index 00000000000..07971cf0b10 Binary files /dev/null and b/docs/images/SplitActivityDiagram.png differ diff --git a/docs/images/SplitSequenceDiagram.png b/docs/images/SplitSequenceDiagram.png new file mode 100644 index 00000000000..efb6fe2224e Binary files /dev/null and b/docs/images/SplitSequenceDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 82c66f8f16e..15ae6b39270 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 91488fd1a0f..08baa416287 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 4bb8b2ce591..d627ed86fad 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/addfExample.png b/docs/images/addfExample.png new file mode 100644 index 00000000000..2ad5b5d1fca Binary files /dev/null and b/docs/images/addfExample.png differ diff --git a/docs/images/addmExample.png b/docs/images/addmExample.png new file mode 100644 index 00000000000..e5e6e3900d3 Binary files /dev/null and b/docs/images/addmExample.png differ diff --git a/docs/images/aliasExample.png b/docs/images/aliasExample.png new file mode 100644 index 00000000000..05cafbf3948 Binary files /dev/null and b/docs/images/aliasExample.png differ diff --git a/docs/images/allocateExample.png b/docs/images/allocateExample.png new file mode 100644 index 00000000000..a5c4c537268 Binary files /dev/null and b/docs/images/allocateExample.png differ diff --git a/docs/images/editmExample.png b/docs/images/editmExample.png new file mode 100644 index 00000000000..cdb2215f6d0 Binary files /dev/null and b/docs/images/editmExample.png differ diff --git a/docs/images/felix-ong.png b/docs/images/felix-ong.png new file mode 100644 index 00000000000..dfe3bfb3179 Binary files /dev/null and b/docs/images/felix-ong.png differ diff --git a/docs/images/findfExample.png b/docs/images/findfExample.png new file mode 100644 index 00000000000..dfb418453fe Binary files /dev/null and b/docs/images/findfExample.png differ diff --git a/docs/images/findmExample.png b/docs/images/findmExample.png new file mode 100644 index 00000000000..fe36e996412 Binary files /dev/null and b/docs/images/findmExample.png differ diff --git a/docs/images/importExample.png b/docs/images/importExample.png new file mode 100644 index 00000000000..6dd74cd1873 Binary files /dev/null and b/docs/images/importExample.png differ diff --git a/docs/images/mac_error.png b/docs/images/mac_error.png new file mode 100644 index 00000000000..6b2a1b04ecd Binary files /dev/null and b/docs/images/mac_error.png differ diff --git a/docs/images/markExample.png b/docs/images/markExample.png new file mode 100644 index 00000000000..1041629444a Binary files /dev/null and b/docs/images/markExample.png differ diff --git a/docs/images/moley456.png b/docs/images/moley456.png new file mode 100644 index 00000000000..de0d8ac0456 Binary files /dev/null and b/docs/images/moley456.png differ diff --git a/docs/images/setmExample.png b/docs/images/setmExample.png new file mode 100644 index 00000000000..b5dcf79f5ff Binary files /dev/null and b/docs/images/setmExample.png differ diff --git a/docs/images/shruthi0310.png b/docs/images/shruthi0310.png new file mode 100644 index 00000000000..17c9e070e1e Binary files /dev/null and b/docs/images/shruthi0310.png differ diff --git a/docs/images/splitExample.png b/docs/images/splitExample.png new file mode 100644 index 00000000000..f0b728e6c1d Binary files /dev/null and b/docs/images/splitExample.png differ diff --git a/docs/images/sportsPA_logo.png b/docs/images/sportsPA_logo.png new file mode 100644 index 00000000000..59228115fa1 Binary files /dev/null and b/docs/images/sportsPA_logo.png differ diff --git a/docs/images/tsinyee.png b/docs/images/tsinyee.png new file mode 100644 index 00000000000..2f20b3f77e0 Binary files /dev/null and b/docs/images/tsinyee.png differ diff --git a/docs/images/yoyociti.png b/docs/images/yoyociti.png new file mode 100644 index 00000000000..17a54630502 Binary files /dev/null and b/docs/images/yoyociti.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..eaa1cfef627 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ --- layout: page -title: AddressBook Level-3 +title: SportsPA --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![codecov](https://codecov.io/gh/AY2122S1-CS2103T-W12-1/tp/branch/master/graph/badge.svg?token=W77BXICS47)](https://codecov.io/gh/AY2122S1-CS2103T-W12-1/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**SportsPA is a desktop application for managing membership and facilities for NUS Sports CCAs.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using SportsPA, head over to the [**User Guide**](UserGuide.html). +* If you are interested about developing SportsPA, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/felix-ong.md b/docs/team/felix-ong.md new file mode 100644 index 00000000000..cfe1604d94e --- /dev/null +++ b/docs/team/felix-ong.md @@ -0,0 +1,53 @@ +--- +layout: page +title: Felix Ong Wei Cong's Project Portfolio Page +--- + +### Project: SportsPA + +SportsPA is a desktop application used to manage membership and training sessions of NUS sports CCAs. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Created availability field and added the ability to add and set member availability. + * What it does: Allows the user to add a new member with the given availability and set the availability of existing members. + * Justification: This feature sets the basis for one of the core features of our application, the splitting of members to facilities based on availability. + * Highlights: Implementing the availability field required some analysis regarding the type of variables to use to represent it to ensure consistency and ease of conversion from user input. The use of the DayOfWeek enum from the java.time package was slightly challenging + due to the initial lack of familiarity with enums and the package. + * Pull Request [\#109](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/109) + +* **New Feature**: Added the ability to allocate and deallocate members from facilities. + * What it does: Allows the user to allocate members to facilities and deallocate members from facilities manually on a given day. + * Justification: Provides an alternative for the user to allocate members other than the split command, as the split command may not provide the ideal allocation mapping of members to facilities. + * Highlights: Changes had to be made to all commands that affects a member as changes to a member may result in an invalid allocations. + For example, there is a need to ensure that a member that has been deleted from the member list is removed from all allocations. + Ensuring that the UI displaying the facilities updates immediately when member-related commands were executed was challenging. + * Pull Request [\#160](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/160) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/#breakdown=true&search=felix-ong) + +
+ +* **Team Tasks**: + * Set up project notes document and suggested initial idea for the project. + * Opened milestone v1.3 and v1.4. + * Updated User Guide to include a section explaining the usage of the User Guide to improve user-friendliness. (Pull request [\#152](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/152)) + +* **Enhancements to existing features**: + * Added realistic constraints to facility-related fields and added tests related to these constraints, improved the format of the displayed values in the UI to improve user experience. + (Pull request [\#196](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/196)) + * Improve `split` feature and related commands by allowing splitting members into facilities on different days and displaying all the allocations by day. Updated existing tests related to the changes made. (Pull request [\#218](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/218)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `listf`,`findf` and `deletef`. (Pull Request [\#66](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/66)) + * Added documentation for the features `allocate` and `deallocate` command. (Pull Request [\#160](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/160)) + * Updated screenshots and documentation for `editm`, `editf`, `split`, `allocate` and `deallocate` commands. (Pull Request [\#227](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/227)) + * Developer Guide: + * Added implementation details of the `split` feature as well as UML diagrams for illustration. (Pull Request [\#265](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/265)) + * Updated Storage section to reflect changes made. (Pull Request [\#240](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/240)) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#98](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/98), [\#89](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/89)) + * Reported bugs and provided suggestions for another team during [PE-D](https://github.com/felix-ong/ped/issues) diff --git a/docs/team/moley456.md b/docs/team/moley456.md new file mode 100644 index 00000000000..145d7afd0ba --- /dev/null +++ b/docs/team/moley456.md @@ -0,0 +1,53 @@ +--- +layout: page +title: Ong Xing Wei's Project Portfolio Page +--- + +### Project: SportsPA + +SportsPA is a desktop application used to manage membership and training sessions of NUS sports CCAs. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to import member details from a CSV file. + * What it does: Allows the user to add and update the details of multiple members without having to repeatedly add members individually from the CLI. + * Justification: This feature improves user experience of the product because instead of repeatedly typing the command word and related fields with their prefixes into the CLI, + a user will be able to reduce the amount he/she types by simply preparing a CSV file and importing the member details from the CSV file. + * Highlights: The implementation was challenging as it required a way to get the details of existing members without + a reference to the member objects such as an index like most of the other commands. Also, behaviour and design choices had to be made to provide + a better user experience. + * Pull request: [\#137](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/137) [\#257](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/257) + +* **New Feature**: Added the ability to export facility details and member allocations to a CSV file. + * What it does: Allows the user to convert the facility details and member allocations into a readable format. + * Justification: This feature improves the product significantly because the user will have a way to share the facility details and allocations + with their CCA members without the need for members to actually have the SportsPA application. + * Highlights: The implementation was somewhat challenging as it required analysis of how the data should be presented in the CSV file. + * Pull request: [\#170](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/170) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/#breakdown=true&search=moley456) + +* **Team tasks**: + * Set up the GitHub team org and repo + * Managed releases `v1.2` - `v1.2b` (2 releases) on GitHub + +* **Enhancements to existing features**: + * Added support for saving and loading facility data to and from the json data file. + * Updated the GUI to accommodate another tab for the facility list and also increased result window height to + accommodate for longer error messages (Pull requests [\#117](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/117), [\#223](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/223)) + * Wrote additional test cases for additional features to increase coverage from 76% to 78% (Pull request [\#273](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/273)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `export` and `import`: [\#137](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/137) [\#173](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/173) + * Did cosmetic tweaks to the table of content and introduction headers: [\#155](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/155) + * Developer Guide: + * Added use cases for new features from v1.3 [\#250](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/250) + * Added instructions for manual testing [\#249](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/249) + * Added Implementation of `import`, including multiple UML diagrams. + * Updated Developer Guide to include Facilities. + +* **Community**: + * Reported bugs and gave suggestions to other teams through [PE-D](https://github.com/Moley456/ped/issues) + diff --git a/docs/team/shruthi0310.md b/docs/team/shruthi0310.md new file mode 100644 index 00000000000..3ab2fa46bab --- /dev/null +++ b/docs/team/shruthi0310.md @@ -0,0 +1,55 @@ +--- +layout: page +title: Senthamaraiselvan Shruthi's Project Portfolio Page +--- + +### Project: SportsPA + +SportsPA is a desktop application used to manage memberships and training sessions for NUS sports CCAs. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to mark/unmark members attendance. + * What it does: allows the user to mark attendance of the members or unmark previously marked attendance. + * Justification: This feature improves the product significantly because the target users require taking attendance of their members and keeping track of the total attendance and hence this command enables them to do so in a quick and easy manner. + * Highlights: This enhancement required an analysis of design alternatives regarding how the total attendance and today attendance will be updated and displayed to the user. + * Pull request: [\#138](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/138) + +* **New Feature**: Added the ability to add facilities to SportsPA. + * What it does: allows the user to add facilities to keep track. + * Justification: This feature improves the product significantly because the target users require managing training sessions by keeping track of the facilities they have booked so that they can allocate members to these facilities using other commands. + * Highlights: This enhancement was difficult to implement as there were various classes needed to represent facilities and their attributes, before the command can be implemented. + * Pull request: [\#96](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/96) + +* **New Feature**: Added the skeleton of ability to split members to facilities. + * What it does: allows the user to add facilities to split members and allocate them to various facilities based on total capacity. + * Justification: This feature improves the product significantly because the target users can easily allocate their members to different locations while ensuring venue capacity is met. + * Highlights: This enhancement was challenging to implement and to test if it worked as it required a command to set availability of the members. But at the time this feature was implemented, + there was no such command implemented yet. So there was a need to create a few dummy availabilities for members in order to test this feature. + * Pull request: [\#112](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/112) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/#breakdown=true&search=shruthi0310) + +* **Team Tasks**: + * Morphed the AddressBook to a different product that supports managing of facilities (e.g `addf`) + * Documented target user profile and some user stories for Developer's Guide + +* **Enhancement to existing features**: + * Improved `deletem` command such that it will update the allocations in facilities every time a member is deleted, instead of + having the users to split again to obtain the updated allocations : [\#216](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/216) + +* **Documentation**: + * User Guide: + * Added documentation for description of User Guide and the features `addf` and `help` and summary table of commands [\#74](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/74) + * Added documentation for the features `mark`, `unmark`, `cleara`: [\#138](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/138) + * Tweaked existing documentation for the features `setm` and `split`: [\#216](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/216) + * Developer Guide: + * Added implementation details of the `mark` and `unmark` feature, including UML diagrams. [\#219](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/219) + * Update Ui section and its diagrams. [\#243](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/243) + * Review DG and make necessary changes to diagrams and texts [\#287](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/287) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#121](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/121), [\#124](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/124), + [\#144](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/144) + * Reported bugs for other groups during PED: [PED](https://github.com/Shruthi0310/ped) diff --git a/docs/team/tsinyee.md b/docs/team/tsinyee.md new file mode 100644 index 00000000000..68382795451 --- /dev/null +++ b/docs/team/tsinyee.md @@ -0,0 +1,51 @@ +--- +layout: page +title: Teo Sin Yee's Project Portfolio Page +--- + +### Project: SportsPA + +SportsPA is a desktop application used to manage membership and training sessions of NUS sports CCAs. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to find members based on multiple attribute keywords. + * What it does: Allows the user to filter the member list by specifying one or more attribute keywords. + * Justification: This feature improves the user experience significantly as users can now find specific members with the specified attribute keywords. This feature essentially filters the member list to the users' liking, thus unimportant data is hidden and only relevant data is displayed to the user. + * Highlights: Implementing this feature was challenging as it required modifications to the parser to parse multiple prefixes and chain multiple predicates. A unique Predicate class was also created for every attribute. + * Pull request: [\#161](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/161) +* **New Feature**: Added the ability to sort the member list. + * What it does: Allows the user to sort the member list, either by name or tags. + * Justification: This feature improves the user experience significantly because the member data can now be arranged into a meaningful order, making it easier for users to analyse and visualise the data. Presentation of the application is also improved since the data can be presented in a systematic manner. + * Highlights: Implementing this feature was rather challenging due to the added complexity of `FilteredList` being an unmodifiable list. It required careful examination of the code base to figure out where the list could be modified. + * Pull request: [\#144](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/144) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=sinyee&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2021-09-17&tabOpen=true&tabType=zoom&zA=tsinyee&zR=AY2122S1-CS2103T-W12-1%2Ftp%5Bmaster%5D&zACS=221.19108280254778&zS=2021-09-17&zFS=w12&zU=2021-11-05&zMG=false&zFTF=commit&zFGS=groupByRepos&zFR=false) + +* **Team Tasks**: + * Managed release `v1.3.1` on GitHub + * Refactored `AddressBook` to `SportsPA` (Pull request [\#241](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/241)) + +* **Enhancements to existing features**: + * Removed unnecessary attributes from Member (Pull request [\#103](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/103)) + * Added facility field validations and related tests (Pull request [\#113](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/113)) + * Fixed bugs from PE-D (Pull request [\#209](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/209)) + * Implemented empty list check for list-related commands and added relevant error messages (Pull request [\#264](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/264)) + * Refactored code to improve code quality and added tests for `AllocateMemberCommandParser` and `DeallocateMemberCommandParser` (Pull request [\#266](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/266)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `addm`, `listm`, `sortm`, `findm` (Pull requests [\#131](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/131), + [\#144](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/144), [\#161](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/161)) + * Added SportsPA logo, "Command Syntax" section and page breaks to improve readability (Pull request [\#266](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/266)) + + * Developer Guide: + * Added implementation details of the `findm` feature. (Pull requests [\#161](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/161), [\#221](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/221)) + * Updated the `Model` section and several UML diagrams to reflect changes made to it for `SportsPA` (Pull request [\#244](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/244)) + * Added "Introduction" and "Using the developer guide" section and defined keyboard inputs by using the HTML keyboard input element to improve readability (Pull requests [\#266](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/266), [\#288](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/288)) + * Added preamble for features (Pull request [\#285](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/285)) + +* **Community**: + * PRs reviewed (with non-trivial review comments): (Pull request [\#105](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/105)) + * Reported bugs and suggestions for other teams through [PE-D](https://github.com/tsinyee/ped/issues) diff --git a/docs/team/yoyociti.md b/docs/team/yoyociti.md new file mode 100644 index 00000000000..83b39ca7110 --- /dev/null +++ b/docs/team/yoyociti.md @@ -0,0 +1,49 @@ +--- +layout: page +title: Seow Xiu Wen's Project Portfolio Page +--- + +### Project: SportsPA + +SportsPA is a desktop application used to manage membership and training sessions of NUS sports CCAs. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Added the ability to create shortcuts for commands (and supporting operations i.e. viewing and deleting created aliases). + * What it does: Allows the user to create aliases for command words. Entering the user-defined shortcut for chosen command word will execute that command. + * Justification: This feature improves user experience of the product significantly because our target users are fast typists well-versed with CLI applications. Aliasing is common in such applications, allowing our users the flexibility of defining their own command words to their preferences. + * Highlights: Implementation was challenging for this enhancement as it required changes to how commands are parsed and commands had to be tracked. + * Pull Request [\#124](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/124) + +* **New Feature**: Added the ability to switch GUI tabs for relevant commands. + * What it does: When users execute facility or member-related commands, the system automatically switches to that tab displaying the correct list (if it is not already on it). + * Justification: Similar to the above new feature, this improves user experience for our target users as users need not manually click on the tabs with their mouse after executing the command. + * Highlights: Changes had to be made to the result of a command execution so that the GUI will be able to use that info to respond accordingly. +Updating tests was also more complicated as many of the commands already relied on the existing implementation of CommandResult, thus had to consider an alternative method (i.e. utility methods) to update tests. + * Pull Request [\#145](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/145) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2122s1.github.io/tp-dashboard/?search=W12-1&sort=groupTitle&sortWithin=title&since=2021-09-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=YoYoCiti&tabRepo=AY2122S1-CS2103T-W12-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false) + +* **Team Tasks**: + * Designed initial mockup for SportsPA v1.1 + * Managed release `v1.2` - `v1.2.1` and `v1.4` (3 releases) on GitHub + * Morphed existing product Address Book to include basic functionality supporting list of facilities (i.e `findf`, `clearf`, `deletef`, `editf`) (Pull requests [\#85](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/85), [\#89](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/89), [\#97](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/97), [\#111](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/111)) + * Fixed bugs discovered from PED (Pull Requests [\#213](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/213), [\#263](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/263)) + +* **Enhancements to existing features**: + * Add test utilities to support Facilities and Facility List for easy and convenient testing (Pull request [\#98](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/98)) + * Improve feature `split` by accounting for instances when zero members are available or if there is insufficient capacity to accommodate all available members (Pull Request [\#150](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/150)) + +* **Documentation**: + * User Guide: + * Added documentation for the features `deletem`,`setm` and `split` (Pull Request [\#66](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/66)) + * Added documentation for the features `alias`, `unalias` and `aliases` command (Pull Request [\#124](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/124)) + * Did cosmetic and tonal tweaks to make User Guide more welcoming and visually aiding (i.e. screenshots, tips) (Pull Requests [\#135](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/135), [\#163](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/163)) + * Developer Guide: + * Added implementation details of the `alias`, `unalias` and `aliases` feature as well as UML diagrams for illustration (Pull Request [\#124](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/124)) + * Review overall updates to Developer Guide Design section and update if necessary (Pull Request [\#259](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/259)) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#112](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/112), [\#137](https://github.com/AY2122S1-CS2103T-W12-1/tp/pull/137) + * Tested and reported bugs for another team during [PED](https://github.com/YoYoCiti/ped/issues) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 8919d8eaa17..24c424033e5 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -28,7 +28,7 @@ package seedu.address.logic.commands; import seedu.address.model.Model; /** - * Changes the remark of an existing person in the address book. + * Changes the remark of an existing member in the address book. */ public class RemarkCommand extends Command { @@ -65,8 +65,8 @@ Following the convention in other commands, we add relevant messages as constant ``` java public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Edits the remark of the person identified " - + "by the index number used in the last person listing. " + + ": Edits the remark of the member identified " + + "by the index number used in the last member listing. " + "Existing remark will be overwritten by the input.\n" + "Parameters: INDEX (must be a positive integer) " + "r/ [REMARK]\n" @@ -101,8 +101,8 @@ public class RemarkCommand extends Command { private final String remark; /** - * @param index of the person in the filtered person list to edit the remark - * @param remark of the person to be updated to + * @param index of the member in the filtered member list to edit the remark + * @param remark of the member to be updated to */ public RemarkCommand(Index index, String remark) { requireAllNonNull(index, remark); @@ -225,11 +225,11 @@ If you are stuck, check out the sample ## Add `Remark` to the model -Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person. +Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of member data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the member’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a member. ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.address.model.member`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-af2f075d24dfcd333876f0fbce321f25). Note how `Remark` has no constrains and thus does not require input validation. @@ -240,9 +240,9 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` ## Add a placeholder element for remark to the UI -Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. +Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each member. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). +Simply add the following to [`seedu.address.ui.MemberCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). **`PersonCard.java`:** @@ -311,9 +311,9 @@ Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/c **`PersonCard.java`:** ``` java -public PersonCard(Person person, int displayedIndex) { +public PersonCard(Person member, int displayedIndex) { //... - remark.setText(person.getRemark().value); + remark.setText(member.getRemark().value); } ``` @@ -340,28 +340,28 @@ save it with `Model#setPerson()`. List lastShownList = model.getFilteredPersonList(); if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = new Person( - personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), - personToEdit.getAddress(), remark, personToEdit.getTags()); + Person memberToEdit = lastShownList.get(index.getZeroBased()); + Person editedMember = new Person( + memberToEdit.getName(), memberToEdit.getPhone(), memberToEdit.getEmail(), + memberToEdit.getAddress(), remark, memberToEdit.getTags()); - model.setPerson(personToEdit, editedPerson); + model.setPerson(memberToEdit, editedMember); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(generateSuccessMessage(editedPerson)); + return new CommandResult(generateSuccessMessage(editedMember)); } /** * Generates a command execution success message based on whether * the remark is added to or removed from - * {@code personToEdit}. + * {@code memberToEdit}. */ - private String generateSuccessMessage(Person personToEdit) { + private String generateSuccessMessage(Person memberToEdit) { String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS; - return String.format(message, personToEdit); + return String.format(message, memberToEdit); } ``` diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..1cbae423dcd 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.address.model.member.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) @@ -100,7 +100,7 @@ In `src/test/data/`, data meant for testing purposes are stored. While keeping t ```json { - "persons": [ { + "members": [ { "name": "Person with invalid name field: Ha!ns Mu@ster", "phone": "9482424", "email": "hans@example.com", diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..14283759546 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -189,22 +189,22 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ @Override public CommandResult execute(Model model) throws CommandException { ... - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + Person memberToEdit = lastShownList.get(index.getZeroBased()); + Person editedMember = createEditedPerson(memberToEdit, editMemberDescriptor); + if (!memberToEdit.isSamePerson(editedMember) && model.hasPerson(editedMember)) { + throw new CommandException(MESSAGE_DUPLICATE_MEMBER); } - model.setPerson(personToEdit, editedPerson); + model.setPerson(memberToEdit, editedMember); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); + return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedMember)); } ``` 1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically, - * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data. - * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked. + * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the member data. + * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ members.
+ FYI, The 'filtered list' is the list of members resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the members so that the user can see the edited member along with all other members. If this was a `find` command, we would be setting that list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of members is being tracked.
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component) @@ -231,7 +231,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ * {@code JsonSerializableAddressBook}. */ public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll( + members.addAll( source.getPersonList() .stream() .map(JsonAdaptedPerson::new) diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..e36058cae70 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -15,16 +15,16 @@ import seedu.address.commons.util.StringUtil; import seedu.address.logic.Logic; import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlySportsPa; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.SportsPa; import seedu.address.model.UserPrefs; import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.storage.JsonSportsPaStorage; import seedu.address.storage.JsonUserPrefsStorage; +import seedu.address.storage.SportsPaStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; import seedu.address.storage.UserPrefsStorage; @@ -36,7 +36,7 @@ */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 0, true); + public static final Version VERSION = new Version(1, 4, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +48,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing SportsPA ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +56,8 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + SportsPaStorage sportsPaStorage = new JsonSportsPaStorage(userPrefs.getSportsPaFilePath()); + storage = new StorageManager(sportsPaStorage, userPrefsStorage); initLogging(config); @@ -69,25 +69,25 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s SportsPA and {@code userPrefs}.
+ * The data from the sample address book will be used instead if {@code storage}'s SportsPA is not found, + * or an empty SportsPA will be used instead if errors occur when reading {@code storage}'s SportsPA. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional sportsPaOptional; + ReadOnlySportsPa initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + sportsPaOptional = storage.readSportsPa(); + if (!sportsPaOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample SportsPA"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = sportsPaOptional.orElseGet(SampleDataUtil::getSampleSportsPa); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty SportsPA"); + initialData = new SportsPa(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty SportsPA"); + initialData = new SportsPa(); } return new ModelManager(initialData, userPrefs); @@ -151,7 +151,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty SportsPA"); initializedPrefs = new UserPrefs(); } @@ -167,13 +167,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting SportsPA " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping SportsPA ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..5baea4f87a0 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -5,9 +5,28 @@ */ public class Messages { + // General Messages public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_EMPTY_LIST = "%1$s list is empty!"; + // Member-specific Messages + public static final String MESSAGE_MEMBER = "Member"; + public static final String MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX = "The member index provided is out of range of" + + " the currently displayed list"; + public static final String MESSAGE_INVALID_MEMBER_DISPLAYED_INDICES = "One or more of the member index provided is" + + " out of range of the currently displayed list"; + public static final String MESSAGE_MEMBERS_LISTED_OVERVIEW = "%1$d members listed!"; + public static final String MESSAGE_MEMBER_NOT_AVAILABLE = "The member at this index is not available on that day"; + public static final String MESSAGE_MEMBER_ALREADY_ALLOCATED = + "The member has already been allocated to that facility on that day"; + public static final String MESSAGE_MEMBER_NOT_ALLOCATED = + "The member is not allocated to that facility on that day"; + + // Facility-specific Messages + public static final String MESSAGE_FACILITY = "Facility"; + public static final String MESSAGE_FACILITIES_LISTED_OVERVIEW = "%1$d facilities listed!"; + public static final String MESSAGE_INVALID_FACILITY_DISPLAYED_INDEX = "The facility index provided is out" + + "of range of the currently displayed list"; + public static final String MESSAGE_FACILITY_AT_MAX_CAPACITY = "The facility provided is already at max capacity"; } diff --git a/src/main/java/seedu/address/commons/util/DayUtil.java b/src/main/java/seedu/address/commons/util/DayUtil.java new file mode 100644 index 00000000000..cf9b603d0bc --- /dev/null +++ b/src/main/java/seedu/address/commons/util/DayUtil.java @@ -0,0 +1,30 @@ +package seedu.address.commons.util; + +import java.time.DayOfWeek; +import java.time.format.TextStyle; +import java.util.Locale; + +/** + * Helper functions for handling days. + */ +public class DayUtil { + /** + * Returns the day of the week given the day number. + * + * @param dayNumber the index of the day to display. + * @return String representing the day of the week. + */ + public static String displayDay(int dayNumber) { + return DayOfWeek.of(dayNumber).getDisplayName(TextStyle.FULL, Locale.getDefault()); + } + + /** + * Returns the day of the week given the day number. + * + * @param day the day-of-week to display. + * @return String representing the day of the week. + */ + public static String displayDay(DayOfWeek day) { + return day.getDisplayName(TextStyle.FULL, Locale.getDefault()); + } +} diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java index 61cc8c9a1cb..e0867f02a25 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/address/commons/util/StringUtil.java @@ -60,9 +60,41 @@ public static boolean isNonZeroUnsignedInteger(String s) { try { int value = Integer.parseInt(s); - return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String) + return value > 0 && !s.startsWith("+") && !s.startsWith("-"); + // "+1" is successfully parsed by Integer#parseInt(String) } catch (NumberFormatException nfe) { return false; } } + + /** + * Returns true if {@code s} represents a non-negative unsigned integer + * e.g. 0, 1, 2, 3, ..., {@code Integer.MAX_VALUE}
+ * Will return false for any other non-null string input + * e.g. empty string, "-1", "+0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isNonNegativeUnsignedInteger(String s) { + requireNonNull(s); + + try { + int value = Integer.parseInt(s); + return value >= 0 && !s.startsWith("+") && !s.startsWith("-"); + // "+1" is successfully parsed by Integer#parseInt(String) + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Returns true if {@code s} represents a boolean value + * Will return false for any other non-null string input + * e.g. empty string, "f", "1", " true " (untrimmed), "t rue" (contains whitespace) + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isValidBooleanValue(String s) { + requireNonNull(s); + return s.equalsIgnoreCase("false") + || s.equalsIgnoreCase("true"); + } } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..3d8be7985db 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -7,8 +7,9 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlySportsPa; +import seedu.address.model.facility.Facility; +import seedu.address.model.member.Member; /** * API of the Logic component @@ -24,19 +25,22 @@ public interface Logic { CommandResult execute(String commandText) throws CommandException, ParseException; /** - * Returns the AddressBook. + * Returns the SportsPa. * - * @see seedu.address.model.Model#getAddressBook() + * @see seedu.address.model.Model#getSportsPa() */ - ReadOnlyAddressBook getAddressBook(); + ReadOnlySportsPa getSportsPa(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of members */ + ObservableList getFilteredMemberList(); + + /** Returns a view of the filitered list of facilities*/ + ObservableList getFilteredFacilityList(); /** * Returns the user prefs' address book file path. */ - Path getAddressBookFilePath(); + Path getSportsPaFilePath(); /** * Returns the user prefs' GUI settings. diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..0af4d93fb50 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -10,11 +10,12 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.SportsPaParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlySportsPa; +import seedu.address.model.facility.Facility; +import seedu.address.model.member.Member; import seedu.address.storage.Storage; /** @@ -26,7 +27,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final SportsPaParser sportsPaParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +35,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + sportsPaParser = new SportsPaParser(); } @Override @@ -42,11 +43,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = sportsPaParser.parseCommand(commandText, model.getAliases()); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveSportsPa(model.getSportsPa()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +56,23 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlySportsPa getSportsPa() { + return model.getSportsPa(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredMemberList() { + return model.getFilteredMemberList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public ObservableList getFilteredFacilityList() { + return model.getFilteredFacilityList(); + } + + @Override + public Path getSportsPaFilePath() { + return model.getSportsPaFilePath(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/AddAliasCommand.java b/src/main/java/seedu/address/logic/commands/AddAliasCommand.java new file mode 100644 index 00000000000..54e18e64244 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddAliasCommand.java @@ -0,0 +1,49 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMAND_WORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SHORTCUT; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.alias.Alias; + +/** + * Adds a user-defined alias to SportsPA user preferences. + */ +public class AddAliasCommand extends Command { + + public static final String COMMAND_WORD = "alias"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": creates an alias for the specified command word.\n" + + "Parameters: " + + PREFIX_SHORTCUT + "SHORTCUT " + + PREFIX_COMMAND_WORD + "COMMAND\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_SHORTCUT + "lf " + + PREFIX_COMMAND_WORD + "listf"; + public static final String MESSAGE_SUCCESS = "Alias for %1$s successfully defined as %2$s"; + + private final Alias alias; + + /** + * Creates {@code AddAliasCommand} object. + */ + public AddAliasCommand(Alias alias) { + requireNonNull(alias); + this.alias = alias; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.addAlias(alias); + return new CommandResult(String.format(MESSAGE_SUCCESS, alias.getCommandWord(), alias.getShortcut())); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) + || (obj instanceof AddAliasCommand + && alias.equals(((AddAliasCommand) obj).alias)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} diff --git a/src/main/java/seedu/address/logic/commands/AddFacilityCommand.java b/src/main/java/seedu/address/logic/commands/AddFacilityCommand.java new file mode 100644 index 00000000000..9c05f3214ca --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddFacilityCommand.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.facility.Facility; + +/** + * Adds a facility to SportsPA. + */ +public class AddFacilityCommand extends Command { + public static final String COMMAND_WORD = "addf"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a facility to SportsPA.\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_LOCATION + "LOCATION " + + PREFIX_TIME + "TIME " + + PREFIX_CAPACITY + "CAPACITY\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Court 1 " + + PREFIX_LOCATION + "University Sports Hall " + + PREFIX_TIME + "1130 " + + PREFIX_CAPACITY + "5 "; + public static final String MESSAGE_SUCCESS = "New facility added: %1$s"; + public static final String MESSAGE_DUPLICATE_FACILITY = "This facility already exists in SportsPA."; + + private final Facility facility; + + /** + * Creates an AddFacilityCommand to add the specified facility. + * + * @param facility Facility to be added. + */ + public AddFacilityCommand(Facility facility) { + requireNonNull(facility); + this.facility = facility; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasFacility(facility)) { + throw new CommandException(MESSAGE_DUPLICATE_FACILITY); + } + + model.addFacility(facility); + return new CommandResult(String.format(MESSAGE_SUCCESS, facility), + false, true, false); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) + || (obj instanceof AddFacilityCommand + && facility.equals(((AddFacilityCommand) obj).facility)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddMemberCommand.java b/src/main/java/seedu/address/logic/commands/AddMemberCommand.java new file mode 100644 index 00000000000..97460571661 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddMemberCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_AVAILABILITY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.member.Member; + +/** + * Adds a member to SportsPA. + */ +public class AddMemberCommand extends Command { + + public static final String COMMAND_WORD = "addm"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a member to SportsPA.\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + "[" + PREFIX_AVAILABILITY + "AVAILABILITY] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "98765432 " + + PREFIX_AVAILABILITY + "1 3 5 " + + PREFIX_TAG + "exco " + + PREFIX_TAG + "y2"; + + public static final String MESSAGE_SUCCESS = "New member added: %1$s"; + public static final String MESSAGE_DUPLICATE_MEMBER = "A member with the same name or phone number " + + "already exists in SportsPA"; + + private final Member toAdd; + + /** + * Creates an AddMemberCommand to add the specified {@code Member} + */ + public AddMemberCommand(Member member) { + requireNonNull(member); + toAdd = member; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasMember(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_MEMBER); + } + + model.addMember(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd), + false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddMemberCommand // instanceof handles nulls + && toAdd.equals(((AddMemberCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AllocateMemberCommand.java b/src/main/java/seedu/address/logic/commands/AllocateMemberCommand.java new file mode 100644 index 00000000000..b81184730a1 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AllocateMemberCommand.java @@ -0,0 +1,112 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_EMPTY_LIST; +import static seedu.address.commons.core.Messages.MESSAGE_FACILITY; +import static seedu.address.commons.core.Messages.MESSAGE_MEMBER; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.commons.util.DayUtil.displayDay; + +import java.time.DayOfWeek; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.facility.AllocationMap; +import seedu.address.model.facility.Facility; +import seedu.address.model.member.Member; + +/** + * Allocates a member available on a particular day to a Facility. + */ +public class AllocateMemberCommand extends Command { + public static final String COMMAND_WORD = "allocate"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Allocates a member to facility.\n" + + "Parameters: " + "MEMBER_INDEX FACILITY_INDEX DAY\n" + + "DAY must be an integer from 1 to 7\n" + + "where 1 represents Monday, 2 represents Tuesday ... and 7 represents Sunday\n" + + "Example: " + COMMAND_WORD + " 1 1 1"; + public static final String MESSAGE_SUCCESS = "Allocated %s to %s for %s"; + + private final Index memberIndex; + private final Index facilityIndex; + private final DayOfWeek day; + + /** + * @param memberIndex Index of the member to be allocated to a facility. + * @param facilityIndex Index of the facility to allocate the member to. + * @param day to allocate the member to the facility. + */ + public AllocateMemberCommand(Index memberIndex, Index facilityIndex, DayOfWeek day) { + requireAllNonNull(memberIndex, facilityIndex, day); + this.memberIndex = memberIndex; + this.facilityIndex = facilityIndex; + this.day = day; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownMemberList = model.getFilteredMemberList(); + List lastShownFacilityList = model.getFilteredFacilityList(); + if (lastShownMemberList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_MEMBER)); + } + if (lastShownFacilityList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_FACILITY)); + } + if (memberIndex.getOneBased() > lastShownMemberList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + if (facilityIndex.getOneBased() > lastShownFacilityList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_FACILITY_DISPLAYED_INDEX); + } + Member toBeAllocated = lastShownMemberList.get(memberIndex.getZeroBased()); + Facility toAllocateTo = lastShownFacilityList.get(facilityIndex.getZeroBased()); + + handleAllocation(toBeAllocated, toAllocateTo, model); + + String dayName = displayDay(day); + return new CommandResult(String.format(MESSAGE_SUCCESS, toBeAllocated.getName(), + toAllocateTo.getName(), dayName), false, true, false); + } + + /** + * Handles the allocation of the member to the facility. + * @param toBeAllocated Member to be allocated. + * @param toAllocateTo Facility to allocate member to. + * @throws CommandException if allocation of the member to the facility is not feasible. + */ + private void handleAllocation(Member toBeAllocated, Facility toAllocateTo, Model model) throws CommandException { + if (toAllocateTo.isMemberAllocatedOnDay(toBeAllocated, day)) { + throw new CommandException(Messages.MESSAGE_MEMBER_ALREADY_ALLOCATED); + } else if (toAllocateTo.isMaxCapacityOnDay(day)) { + throw new CommandException(Messages.MESSAGE_FACILITY_AT_MAX_CAPACITY); + } else if (!toBeAllocated.isAvailableOnDay(day.getValue())) { + throw new CommandException(Messages.MESSAGE_MEMBER_NOT_AVAILABLE); + } + AllocationMap updatedAllocationMap = toAllocateTo.getAllocationMap(); + updatedAllocationMap.addMemberOnDay(toBeAllocated, day); + Facility afterAllocated = new Facility( + toAllocateTo.getName(), toAllocateTo.getLocation(), toAllocateTo.getTime(), toAllocateTo.getCapacity(), + updatedAllocationMap); + model.setFacility(toAllocateTo, afterAllocated); + model.updateFilteredFacilityList(Model.PREDICATE_SHOW_ALL_FACILITIES); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof AllocateMemberCommand)) { + return false; + } + + AllocateMemberCommand e = (AllocateMemberCommand) other; + return memberIndex.equals(e.memberIndex) && facilityIndex.equals(e.facilityIndex) && day.equals(e.day); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearAttendanceCommand.java b/src/main/java/seedu/address/logic/commands/ClearAttendanceCommand.java new file mode 100644 index 00000000000..bcf075f57d5 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearAttendanceCommand.java @@ -0,0 +1,28 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Clears today's attendance for all members in SportsPA. + */ +public class ClearAttendanceCommand extends Command { + public static final String COMMAND_WORD = "cleara"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Clears today's attendance for all members\n" + + "Example: " + COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Cleared today's attendance"; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.getInternalMemberList().isEmpty()) { + throw new CommandException(String.format(Messages.MESSAGE_EMPTY_LIST, Messages.MESSAGE_MEMBER)); + } + model.resetTodayAttendance(); + return new CommandResult(MESSAGE_SUCCESS, false, false, true); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/ClearFacilitiesCommand.java b/src/main/java/seedu/address/logic/commands/ClearFacilitiesCommand.java new file mode 100644 index 00000000000..3d2e6a737ad --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearFacilitiesCommand.java @@ -0,0 +1,26 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Clears the facility list of SportsPA. + */ +public class ClearFacilitiesCommand extends Command { + + public static final String COMMAND_WORD = "clearf"; + public static final String MESSAGE_SUCCESS = "Facility list has been cleared!"; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.getInternalFacilityList().isEmpty()) { + throw new CommandException(String.format(Messages.MESSAGE_EMPTY_LIST, Messages.MESSAGE_FACILITY)); + } + model.resetFacilityList(); + return new CommandResult(MESSAGE_SUCCESS, false, true, false); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearMembersCommand.java b/src/main/java/seedu/address/logic/commands/ClearMembersCommand.java new file mode 100644 index 00000000000..406c48c8943 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearMembersCommand.java @@ -0,0 +1,28 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Clears the member list of SportsPA. + */ +public class ClearMembersCommand extends Command { + + public static final String COMMAND_WORD = "clearm"; + public static final String MESSAGE_SUCCESS = "Member list has been cleared!"; + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.getInternalMemberList().isEmpty()) { + throw new CommandException(String.format(Messages.MESSAGE_EMPTY_LIST, Messages.MESSAGE_MEMBER)); + } + model.resetMemberList(); + + return new CommandResult(MESSAGE_SUCCESS, false, false, true); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..1554df87063 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -7,7 +7,6 @@ * Represents a command with hidden internal logic and the ability to be executed. */ public abstract class Command { - /** * Executes the command and returns the result message. * diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..a6812bcd172 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -17,13 +17,22 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + /** The application should display Facility Tab */ + private final boolean showFacilityTab; + + /** The application should display Member Tab */ + private final boolean showMemberTab; + /** - * Constructs a {@code CommandResult} with the specified fields. + * Constructs a {@code CommandResult} with all its fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, + boolean showFacilityTab, boolean showMemberTab) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.showFacilityTab = showFacilityTab; + this.showMemberTab = showMemberTab; } /** @@ -31,7 +40,26 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, + false, false); + } + + /** + * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, {@code showHelp} and {@code exit}, + * and other fields set to their default value. + */ + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + this(feedbackToUser, showHelp, exit, false, false); + } + + /** + * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, + * {@code showFacilityTab}, {@code showMemberTab}, and other fields set to their default value. + */ + public CommandResult(String feedbackToUser, boolean isHelpOrExit, boolean showFacilityTab, boolean showMemberTab) { + this(feedbackToUser, false, false, showFacilityTab, showMemberTab); + assert !isHelpOrExit; + assert showFacilityTab != showMemberTab; } public String getFeedbackToUser() { @@ -46,6 +74,14 @@ public boolean isExit() { return exit; } + public boolean isShowFacilityTab() { + return showFacilityTab; + } + + public boolean isShowMemberTab() { + return showMemberTab; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -60,12 +96,14 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && exit == otherCommandResult.exit + && showFacilityTab == otherCommandResult.showFacilityTab + && showMemberTab == otherCommandResult.showMemberTab; } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, exit, showFacilityTab, showMemberTab); } } diff --git a/src/main/java/seedu/address/logic/commands/DeallocateMemberCommand.java b/src/main/java/seedu/address/logic/commands/DeallocateMemberCommand.java new file mode 100644 index 00000000000..956c104b3f4 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeallocateMemberCommand.java @@ -0,0 +1,110 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_EMPTY_LIST; +import static seedu.address.commons.core.Messages.MESSAGE_FACILITY; +import static seedu.address.commons.core.Messages.MESSAGE_MEMBER; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.commons.util.DayUtil.displayDay; + +import java.time.DayOfWeek; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.facility.AllocationMap; +import seedu.address.model.facility.Facility; +import seedu.address.model.member.Member; + +/** + * Deallocates a member available on a particular day from a Facility. + */ +public class DeallocateMemberCommand extends Command { + public static final String COMMAND_WORD = "deallocate"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deallocates a member from a facility.\n" + + "Parameters: " + "MEMBER_INDEX FACILITY_INDEX DAY\n" + + "DAY must be an integer from 1 to 7\n" + + "where 1 represents Monday, 2 represents Tuesday ... and 7 represents Sunday\n" + + "Example: " + COMMAND_WORD + " 1 1 1"; + public static final String MESSAGE_SUCCESS = "Deallocated %s from %s for %s"; + + private final Index memberIndex; + private final Index facilityIndex; + private final DayOfWeek day; + + /** + * @param memberIndex of the member to be deallocated from a facility. + * @param facilityIndex of the facility to deallocate the member from. + * @param day to deallocate the member from the facility. + */ + public DeallocateMemberCommand(Index memberIndex, Index facilityIndex, DayOfWeek day) { + requireAllNonNull(memberIndex, facilityIndex, day); + this.memberIndex = memberIndex; + this.facilityIndex = facilityIndex; + this.day = day; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownMemberList = model.getFilteredMemberList(); + List lastShownFacilityList = model.getFilteredFacilityList(); + if (lastShownMemberList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_MEMBER)); + } + if (lastShownFacilityList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_FACILITY)); + } + if (memberIndex.getOneBased() > lastShownMemberList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + if (facilityIndex.getOneBased() > lastShownFacilityList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_FACILITY_DISPLAYED_INDEX); + } + Member toBeDeallocated = lastShownMemberList.get(memberIndex.getZeroBased()); + Facility toDeallocateFrom = lastShownFacilityList.get(facilityIndex.getZeroBased()); + + handleDeallocation(toBeDeallocated, toDeallocateFrom, model); + + String dayName = displayDay(day); + return new CommandResult(String.format(MESSAGE_SUCCESS, toBeDeallocated.getName(), + toDeallocateFrom.getName(), dayName), false, true, false); + } + + /** + * Handles the allocation of the member to the facility. + * @param toBeDeallocated Member to be deallocated from the facility. + * @param toDeallocateFrom Facility to deallocate the member from. + * @throws CommandException if deallocation of the member from the facility is not feasible. + */ + private void handleDeallocation(Member toBeDeallocated, + Facility toDeallocateFrom, Model model) throws CommandException { + if (!toDeallocateFrom.isMemberAllocatedOnDay(toBeDeallocated, day)) { + throw new CommandException(Messages.MESSAGE_MEMBER_NOT_ALLOCATED); + } else { + AllocationMap updatedAllocationMap = toDeallocateFrom.getAllocationMap(); + updatedAllocationMap.removeMemberOnDay(toBeDeallocated, day); + Facility afterDeallocated = new Facility( + toDeallocateFrom.getName(), toDeallocateFrom.getLocation(), toDeallocateFrom.getTime(), + toDeallocateFrom.getCapacity(), updatedAllocationMap); + model.setFacility(toDeallocateFrom, afterDeallocated); + model.updateFilteredFacilityList(Model.PREDICATE_SHOW_ALL_FACILITIES); + } + + } + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof DeallocateMemberCommand)) { + return false; + } + + DeallocateMemberCommand e = (DeallocateMemberCommand) other; + return memberIndex.equals(e.memberIndex) && facilityIndex.equals(e.facilityIndex) && day.equals(e.day); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteAliasCommand.java b/src/main/java/seedu/address/logic/commands/DeleteAliasCommand.java new file mode 100644 index 00000000000..937eab4af8c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteAliasCommand.java @@ -0,0 +1,49 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.alias.CommandWord; +import seedu.address.model.alias.Shortcut; + +/** + * Deletes user-defined alias from SportsPA user preferences. + */ +public class DeleteAliasCommand extends Command { + public static final String COMMAND_WORD = "unalias"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes specified alias (case-sensitive).\n" + + "Parameters: " + + "SHORTCUT\n" + + "Example: " + COMMAND_WORD + " lf"; + public static final String MESSAGE_ALIAS_DOES_NOT_EXIST = "The given alias '%1$s' does not exist."; + public static final String MESSAGE_SUCCESS = "Alias for %1$s defined as %2$s successfully removed."; + + private final Shortcut shortcut; + + /** + * Creates {@code DeleteAlias} object. + */ + public DeleteAliasCommand(Shortcut shortcut) { + requireNonNull(shortcut); + this.shortcut = shortcut; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + CommandWord commandWord = model.removeAlias(shortcut); + if (isNull(commandWord)) { + throw new CommandException(String.format(MESSAGE_ALIAS_DOES_NOT_EXIST, shortcut)); + } + return new CommandResult(String.format(MESSAGE_SUCCESS, commandWord, shortcut)); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) + || (obj instanceof DeleteAliasCommand + && shortcut.equals(((DeleteAliasCommand) obj).shortcut)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/DeleteFacilityCommand.java b/src/main/java/seedu/address/logic/commands/DeleteFacilityCommand.java new file mode 100644 index 00000000000..9c56281c335 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteFacilityCommand.java @@ -0,0 +1,55 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_EMPTY_LIST; +import static seedu.address.commons.core.Messages.MESSAGE_FACILITY; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.facility.Facility; + +public class DeleteFacilityCommand extends Command { + + public static final String COMMAND_WORD = "deletef"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the facility identified by the index number used in the currently displayed facility list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_FACILITY_SUCCESS = "Deleted Facility: %1$s"; + + private final Index targetIndex; + + public DeleteFacilityCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownFacilityList = model.getFilteredFacilityList(); + if (lastShownFacilityList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_FACILITY)); + } + if (targetIndex.getZeroBased() >= lastShownFacilityList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_FACILITY_DISPLAYED_INDEX); + } + + Facility facilityToDelete = lastShownFacilityList.get(targetIndex.getZeroBased()); + model.deleteFacility(facilityToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_FACILITY_SUCCESS, facilityToDelete), + false, true, false); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteFacilityCommand + && targetIndex.equals(((DeleteFacilityCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java b/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java new file mode 100644 index 00000000000..7f9ebb8fd44 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteMemberCommand.java @@ -0,0 +1,59 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_EMPTY_LIST; +import static seedu.address.commons.core.Messages.MESSAGE_MEMBER; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.member.Member; + +/** + * Deletes a member identified using it's displayed index from SportPA's member list. + */ +public class DeleteMemberCommand extends Command { + + public static final String COMMAND_WORD = "deletem"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the member identified by the index number used in the currently displayed member list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_MEMBER_SUCCESS = "Deleted Member: %1$s"; + + private final Index targetIndex; + + public DeleteMemberCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredMemberList(); + if (lastShownList.isEmpty()) { + throw new CommandException(String.format(MESSAGE_EMPTY_LIST, MESSAGE_MEMBER)); + } + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + + Member memberToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteMember(memberToDelete); + model.updateFilteredFacilityList(Model.PREDICATE_SHOW_ALL_FACILITIES); + return new CommandResult(String.format(MESSAGE_DELETE_MEMBER_SUCCESS, memberToDelete), + false, false, true); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteMemberCommand // instanceof handles nulls + && targetIndex.equals(((DeleteMemberCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { - - public static final String COMMAND_WORD = "edit"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } -} diff --git a/src/main/java/seedu/address/logic/commands/EditFacilityCommand.java b/src/main/java/seedu/address/logic/commands/EditFacilityCommand.java new file mode 100644 index 00000000000..be58fbf199c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditFacilityCommand.java @@ -0,0 +1,204 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOCATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_FACILITIES; + +import java.time.DayOfWeek; +import java.util.List; +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.facility.AllocationMap; +import seedu.address.model.facility.Capacity; +import seedu.address.model.facility.Facility; +import seedu.address.model.facility.FacilityName; +import seedu.address.model.facility.Location; +import seedu.address.model.facility.Time; + +public class EditFacilityCommand extends Command { + + public static final String COMMAND_WORD = "editf"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the facility identified " + + "by the index number used in the currently displayed facility list.\n" + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_LOCATION + "LOCATION] " + + "[" + PREFIX_TIME + "TIME] " + + "[" + PREFIX_CAPACITY + "CAPACITY]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_LOCATION + "Kent Ridge Sports Hall 5"; + + public static final String MESSAGE_EDIT_FACILITY_SUCCESS = "Edited Facility: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_FACILITY = "This facility already exists in SportsPA"; + + private final Index index; + private final EditFacilityDescriptor editFacilityDescriptor; + + /** + * Creates EditFacilityCommand object. + * + * @param index of the facility in the filtered facility list to edit. + * @param editFacilityDescriptor details to edit the facility with. + */ + public EditFacilityCommand(Index index, EditFacilityDescriptor editFacilityDescriptor) { + requireAllNonNull(index, editFacilityDescriptor); + + this.index = index; + this.editFacilityDescriptor = new EditFacilityDescriptor(editFacilityDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredFacilityList(); + if (lastShownList.isEmpty()) { + throw new CommandException(String.format(Messages.MESSAGE_EMPTY_LIST, Messages.MESSAGE_FACILITY)); + } + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_FACILITY_DISPLAYED_INDEX); + } + + Facility facilityToEdit = lastShownList.get(index.getZeroBased()); + Facility editedFacility = createEditedFacility(facilityToEdit, editFacilityDescriptor); + + if (!facilityToEdit.isSameFacility(editedFacility) && model.hasFacility(editedFacility)) { + throw new CommandException(MESSAGE_DUPLICATE_FACILITY); + } + + for (DayOfWeek day : DayOfWeek.values()) { + if (facilityToEdit.getCapacityAsOnDay(day) > editedFacility.getMaxCapacityOnDay(day)) { + facilityToEdit.clearAllocationMapOnDay(day); + } + } + + model.setFacility(facilityToEdit, editedFacility); + model.updateFilteredFacilityList(PREDICATE_SHOW_ALL_FACILITIES); + return new CommandResult(String.format(MESSAGE_EDIT_FACILITY_SUCCESS, editedFacility), + false, true, false); + } + + /** + * Creates and returns a {@code Facility} with the details of {@code facilityToEdit} + * edited with {@code editFacilityDescriptor}. + * + * @param editFacilityDescriptor details to edit the facility with. + * @param facilityToEdit the facility to edit. + * @return the facility with the edited details. + */ + private static Facility createEditedFacility(Facility facilityToEdit, + EditFacilityDescriptor editFacilityDescriptor) { + assert facilityToEdit != null; + + FacilityName updatedName = editFacilityDescriptor.getFacilityName().orElse(facilityToEdit.getName()); + Location updatedLocation = editFacilityDescriptor.getLocation().orElse(facilityToEdit.getLocation()); + Time updatedTime = editFacilityDescriptor.getTime().orElse(facilityToEdit.getTime()); + Capacity updatedCapacity = editFacilityDescriptor.getCapacity().orElse(facilityToEdit.getCapacity()); + // edit command does not allow editing of allocations + AllocationMap allocationMap = facilityToEdit.getAllocationMap(); + + return new Facility(updatedName, updatedLocation, updatedTime, updatedCapacity, allocationMap); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof EditFacilityCommand + && index.equals(((EditFacilityCommand) other).index) + && editFacilityDescriptor.equals(((EditFacilityCommand) other).editFacilityDescriptor)); + } + + /** + * Stores the details to edit the facility with. Each non-empty field will replace the + * corresponding field value of the facility. + */ + public static class EditFacilityDescriptor { + private FacilityName facilityName; + private Location location; + private Time time; + private Capacity capacity; + + public EditFacilityDescriptor() {} + + /** + * Copy constructor. + */ + public EditFacilityDescriptor(EditFacilityDescriptor toCopy) { + setFacilityName(toCopy.facilityName); + setLocation(toCopy.location); + setTime(toCopy.time); + setCapacity(toCopy.capacity); + } + + /** + * Checks if at least one field is edited. + * + * @return true if at least one field is edited, false is otherwise. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(facilityName, location, time, capacity); + } + + public void setFacilityName(FacilityName facilityName) { + this.facilityName = facilityName; + } + + public Optional getFacilityName() { + return Optional.ofNullable(facilityName); + } + + public void setLocation(Location location) { + this.location = location; + } + + public Optional getLocation() { + return Optional.ofNullable(location); + } + + public void setCapacity(Capacity capacity) { + this.capacity = capacity; + } + + public Optional getCapacity() { + return Optional.ofNullable(capacity); + } + + public void setTime(Time time) { + this.time = time; + } + + public Optional