diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fd8c44d086..2a82dc31b1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -13,11 +13,6 @@ jobs: - name: Set up repository uses: actions/checkout@master - - name: Set up repository - uses: actions/checkout@master - with: - ref: master - - name: Merge to master run: git checkout --progress --force ${{ github.sha }} @@ -30,6 +25,16 @@ jobs: java-version: '11' java-package: jdk+fx + # Install dos2unix on Linux + - name: Install dos2unix (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y dos2unix + + # Install dos2unix on macOS + - name: Install dos2unix (macOS) + if: runner.os == 'macOS' + run: brew install dos2unix + - name: Build and check with Gradle run: ./gradlew check @@ -47,4 +52,4 @@ jobs: if: always() && runner.os == 'Windows' working-directory: ${{ github.workspace }}/text-ui-test shell: cmd - run: runtest.bat \ No newline at end of file + run: runtest.bat diff --git a/.gitignore b/.gitignore index 2873e189e1..13f568e964 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,18 @@ src/main/resources/docs/ *.iml bin/ -/text-ui-test/ACTUAL.TXT + +text-ui-test/runtest.bat +text-ui-test/runtest.sh +WildWatch.log +text-ui-test/ACTUAL.TXT +META-INF/MANIFEST.MF text-ui-test/EXPECTED-UNIX.TXT +WildWatch.log.1 +WildWatch.log.1.lck +WildWatch.log.lck +WildWatch.txt +WildWatch Entry Collection.txt +~$System Architecture.pptx +System Architecture.pptx +*.csv diff --git a/README.md b/README.md index f82e2494b7..9238eac510 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,14 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 11 (use the exact version), update Intellij to the most recent version. - -1. **Ensure Intellij JDK 11 is defined as an SDK**, as described [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project. -1. **Import the project _as a Gradle project_**, as described [here](https://se-education.org/guides/tutorials/intellijImportGradleProject.html). -1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/duke/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: - ``` - > Task :compileJava - > Task :processResources NO-SOURCE - > Task :classes - - > Task :Duke.main() - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - - What is your name? - ``` - Type some word and press enter to let the execution proceed to the end. - -## Build automation using Gradle - -* This project uses Gradle for build automation and dependency management. It includes a basic build script as well (i.e. the `build.gradle` file). -* If you are new to Gradle, refer to the [Gradle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/gradle.html). - -## Testing - -### I/O redirection tests - -* To run _I/O redirection_ tests (aka _Text UI tests_), navigate to the `text-ui-test` and run the `runtest(.bat/.sh)` script. - -### JUnit tests - -* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template. -* If you are new to JUnit, refer to the [JUnit Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/junit.html). - -## Checkstyle - -* A sample CheckStyle rule configuration is provided in this project. -* If you are new to Checkstyle, refer to the [Checkstyle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/checkstyle.html). - -## CI using GitHub Actions - -The project uses [GitHub actions](https://github.com/features/actions) for CI. When you push a commit to this repo or PR against it, GitHub actions will run automatically to build and verify the code as updated by the commit/PR. - -## Documentation - -`/docs` folder contains a skeleton version of the project documentation. - -Steps for publishing documentation to the public: -1. If you are using this project template for an individual project, go your fork on GitHub.
- If you are using this project template for a team project, go to the team fork on GitHub. -1. Click on the `settings` tab. -1. Scroll down to the `GitHub Pages` section. -1. Set the `source` as `master branch /docs folder`. -1. Optionally, use the `choose a theme` button to choose a theme for your documentation. +# WildWatch + +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/6c982946-3165-41ec-917a-54c46c30b012) + +Welcome aboard fellow animal lover! We are really excited to have you here! 😉 +Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the Command Line Interface (CLI). +The purpose of this user guide is to familiarize you with the program and help you when you face a problem using it. +Its main job is to store and present animal data, this would make your job so much more convenient! + +Useful links: +* [About Us](https://ay2324s1-cs2113t-w11-2.github.io/tp/AboutUs.html) +* [Demo Video](https://www.youtube.com/watch?v=ouKIavNNR5k) +* [User Guide](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html) +* [Developer Guide](https://ay2324s1-cs2113t-w11-2.github.io/tp/DeveloperGuide.html) diff --git a/build.gradle b/build.gradle index ea82051fab..ee116e101a 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,8 @@ repositories { dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.11.0' + testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.11.0' } test { @@ -29,11 +31,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("seedu.wildwatch.WildWatch") } shadowJar { - archiveBaseName.set("duke") + archiveBaseName.set("wildwatch") archiveClassifier.set("") } @@ -43,4 +45,5 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..3ce6c47e5a 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,8 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +| Display | Name | Github Profile | Portfolio | +|-------------------------------------------------------------------------------------------|:-------------:|:----------------------------------------:|:------------------------------------------------------------------------------------------:| +| ![](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/252865bb-811e-48b1-9777-6c01b98500b1) | Lee Sungmin | [Github](https://github.com/woodenclock) | [Portfolio](https://ay2324s1-cs2113t-w11-2.github.io/tp/team/woodenclock.html) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Tang Zhen En | [Github](https://github.com/tangzhenen) | [Portfolio](https://ay2324s1-cs2113t-w11-2.github.io/tp/team/tangzhenen.html) | +| ![](images/person.png) | Lien Cai Ting | [Github](https://github.com/lctxct) | [Portfolio](https://github.com/AY2324S1-CS2113T-W11-2/tp/blob/master/docs/team/caiting.md) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Jeremy | [Github](#) | [Portfolio](#) | diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..2925e508da 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,38 +1,375 @@ -# Developer Guide +# WildWatch Developer Guide 🦒 -## Acknowledgements +## Table of Contents +- [Introduction](#introduction-) + - [Purpose](#purpose) + - [Audience](#audience) + - [How to use the Developer Guide](#how-to-use-the-developer-guide) + - [Legend](#legend) +- [Quick Start](#quick-start-) +- [Design & Implementation](#design--implementation-) + - [System Architecture](#system-architecture) + - [Main Component](#main-component) + - [UI Package](#ui-package) + - [Parser Package](#parser-package) + - [Command Package](#command-package) + - [EntryList Package](#entrylist-package) + - [Entry Package](#entry-package) + - [Storage Package](#storage-package) +- [Product Scope](#product-scope-) + - [Target User Profile](#target-user-profile) + - [Value Proposition](#value-proposition) +- [User Stories](#user-stories-) +- [Non-Functional Requirements](#non-functional-requirements-) +- [Manual Testing](#manual-testing-) +- [Command Summary](#command-summary-%EF%B8%8F) +- [Glossary](#glossary-) +- [Acknowledgements](#acknowledgements-) -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +-------------------------------------------------------------------------------------------------------------------------------------- +
-## Design & implementation +## Introduction 🐻 -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +Welcome aboard fellow animal lover! We are really excited to have you here! 😉 Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the [Command Line Interface (CLI)](#glossary-). Its main job is to store and present animal data required in everyday operations of the wildlife reserve. +### Purpose +This document specifies the architectural and software design decisions in the implementation of the WildWatch. +For a detailed guide on how to use the program and explanations on the individual commands, we have our very own [**WildWatch User Guide**](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html) for your perusal. + +### Audience +The intended audience for this document are developers who would like to look under the hood and understand how WildWatch works, and are looking to introduce new functionalities into WildWatch. + +### How to use the Developer Guide +- Are you new here? +No worries, head to the [Quick Start](#quick-start-) page. +- Lost among the pages? +Head to the [Table of Contents](#table-of-contents) to look for the right pages. +- Need help with the functionalities? +Head to the [Features](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html#features-) page in the User Guide for detailed guidance. +- Do you have a question for us? +Head to the [FAQ](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html#faq-) page in the User Guide. +- Do you have a bug to report? +Head to the [Bug Reporting](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html#bug-reporting-) page in the User Guide. +- Do you want a concise summary of all functionalities? +Head to the [Command Summary](#command-summary-%EF%B8%8F) page for a summary of all commands. +- Not sure what that word meant? +Head to the [Glossary](#glossary-) page for its meaning. + +[⬆ Back to top](#table-of-contents) + +
+ +### Legend + +| Symbol | Meaning | +| -------------- |------------------------------------------------------------ | +| ❗ IMPORTANT | These are important instructions that you should follow. | +| ✏ Note | These are important details that you should take note of. | +| ⬆ Back to top | Click to scroll back up to the `Table of Contents`. | +| 🐵 🦊 🦁 | Animals indicate you have reached a new section. | + +> ❗ IMPORTANT +> * Capitalized words between angle brackets `< >` is a field to be filled up appropriately by the user. +> * E.g., `delete ` should be `delete 2` +> * Anything between curly brackets `{ }` is an optional field that may or may not be filled up by the user. +> * E.g., `summary {}` could be `summary lion` or `summary`. + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Quick Start 🐵 + +1. Ensure you have Java 11 or above installed in your Computer. (How do I install [Java 11](https://www.oracle.com/java/technologies/downloads/#java11)? What is my [Java version](https://www.java.com/en/download/help/version_manual.html)?) + +2. Download the latest `WildWatch.jar` from [here](https://github.com/AY2324S1-CS2113T-W11-2/tp/releases). + +3. Copy the file into the folder you want to use as the [home folder](#glossary-) for your WildWatch. + +4. Open a [command terminal](https://www.freecodecamp.org/news/command-line-for-beginners/), `cd` into the home folder you put the jar file in, and run the following command: `java -jar WildWatch.jar`. +You should see the welcome screen as the diagram below. + +5. Type commands beside `>>>` and press `Enter` to execute it. +(e.g. typing `help` and pressing `Enter` will show the help page). + +``` +____________________________________________________________ +____ __ ____ __ __ _______ +\ \ / \ / / | | | | | \ + \ \/ \/ / | | | | | .--. | + \ / | | | | | | | | + \ /\ / | | | `----.| '--' | + \__/ \__/ |__| |_______||_______/ +____ __ ____ ___ .___________. ______ __ __ +\ \ / \ / / / \ | | / || | | | + \ \/ \/ / / ^ \ `---| |----`| ,----'| |__| | + \ / / /_\ \ | | | | | __ | + \ /\ / / _____ \ | | | `----.| | | | + \__/ \__/ /__/ \__\ |__| \______||__| |__| +____________________________________________________________ +Hello there! Welcome to WildWatch! + +Checking if "WildWatch.txt" already exists... +File does not exist. +Creating new file... +File created successfully. +What would you like to do? +____________________________________________________________ +>>> +``` + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Design & Implementation 🐸 + +### System Architecture + +![System Architecture](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/90309a3c-f784-4ffb-8eef-65735c05ec52) + +The **_Architecture Diagram_** above shows a high-level overview of the architectural design of WildWatch. +Actual detailed implementation may differ from the architectural diagram. +(e.g., `Parser` in the diagram encompasses the `Execute`, `Error`, and `Parser` packages). + +WildWatch is comprised of 7 major components. +- `Main`: Entry point of the program. +- `UI`: A package that receives input from, and prints output to the user. +- `Parser`: A package that processes and interprets the input command of the user. +- `Command`: A package that does specific task according to the command of the user. +- `EntryList`: A package that stores all the wildlife data, while the program is running. +- `Entry`: A package that stores individual wildlife data, while the program is running. +- `Storage`: A package that saves and retrieves all the wildlife data from the local storage + +[⬆ Back to top](#table-of-contents) + +![General Sequence Diagram](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/6cef0c5a-c924-4f20-bf82-3ccee9eb61c0) + +The **_Generic Sequence Diagram_** above shows how the components in the architecture interact with each other for a generic command input in WildWatch. +> ✏ Note: `Command` here is a placeholder, and will be replaced by actual commands. e.g., `ListCommand`. + +### Main Component +The `Main` component, residing as a method in the `WildWatch` class, which is the only class without a package, is the entry point of the program. + +![Main Class Diagram](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/2745b29a-da1d-423e-b460-10f65dbc8e0d) + +[⬆ Back to top](#table-of-contents) + +
+ +### UI Package +The `UI` components can be found within the `UI` package. +It prompts and reads commands from the user and sends the command to `Execute` package to be executed. +Lastly, it prints an output message upon completion of the command, to show the success of failure of the command execution. + +![UI Class Diagram](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/d44324ea-ca0a-4094-8082-32ddafc7e694) + +[⬆ Back to top](#table-of-contents) + +
+ +### Parser Package +The `Parser` components can be found within the `Parser` package. +It is responsible for parsing the input String of the user, and returning an appropriate `XYZCommand` class. +If the input is invalid, it throws exceptions to the `Error` package for error handling. +The **_Parser Class Diagram_** below shows how `Execute`, `Parser`, `Error`, `Command` classes of their respective packages work together. + +![Parser Class Diagram](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/1c8a8ae1-a14d-427f-8ecd-aa5038fb4fc7) + +[⬆ Back to top](#table-of-contents) + +
+ +### Command Package +The `Command` components can be found within the `Command` package. + +![Command Sequence Diagram](images/AddSequenceDiagram.png) +The above sequence diagram depicts how WildWatch executes an `add` command. + +### EntryList Package +The `EntryList` class can be found within the `Entry` package. +It is resposible for storing all the entry data in an static `ArrayList` class. +It is responsible for "getting" and "setting" values of individual `Entry` objects. + +[⬆ Back to top](#table-of-contents) + +
+ +### Entry Package +The `Entry` class can be found within the `Entry` package. +Each `Entry` object holds the data of a single animal entry, residing in the static `ArrayList` maintained by `EntryList` class. + +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/ee46913d-dfc5-4fd5-85ef-53bb03546085) +The class diagram above shows the relationship beteen the `Entry`, `EntryList`, and `ArrayList` classes. + +[⬆ Back to top](#table-of-contents) +
+ +### Storage Package +The `Storage` components can be found within the `Storage` package. +The components work together to read from previously saved file in the local memory. +The entry data is then loaded into the program. +If a valid command that changes the entry data is executed, the relevant changes are saved onto the local file. + +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/e40d9399-a56e-4d4f-953a-024e1252054f) +The class diagram above shows the relationship between the different classes in the `Storage` package. + + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Product scope 🦁 -## Product scope ### Target user profile -{Describe the target user profile} +Target user profile for WildWatch is the clerks working in wildlife reserves, who manages the wildlife data. +Since our program is interacted through the [Command Line Interface (CLI)](#glossary-), someone who can type fast will definitely benefit from WildWatch. +This would significantly boost one's productivity. +Since clerks are already accustomed to typing and working with a computer, this program would be ideal for this user profile. + ### Value proposition -{Describe the value proposition: what problem does it solve?} +- **Effortless User Experience**: Designed with simplicity in mind, our product ensures that even users with minimal technical experience can easily navigate and interact with the software. +- **Rapid CLI Interaction**: Unlike traditional GUIs that require multiple clicks and drags, our Command Line Interface (CLI) offers direct command inputs, allowing for faster and more efficient operations. +- **Light & Versatile**: Built to be lean and light, our program ensures seamless performance across a variety of devices, as long as Java is installed, from high-end workstations to older laptops. Regardless of your computer's specifications, our product guarantees a smooth and efficient functioning. +- **Robust Data Protection**: Say goodbye to the vulnerabilities of paper records! Our digital solution offers enhanced data protection, ensuring your records remain safe from damage, loss, or unauthorized access. With advanced encryption and backup mechanisms in place, your data's safety is our priority. +- **Eco-Friendly**: Transition from paper to digital and contribute to a greener planet. Not only does this transition reduce clutter and the risk of loss, but it also significantly diminishes your carbon footprint. +- **Cost-Effective**: Eliminate the recurring costs of paper, printing, and storage. With our solution, you'll experience a noticeable reduction in operational expenses while benefiting from advanced data management capabilities. + +[⬆ Back to top](#table-of-contents) -## User Stories +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## User Stories 🦊 |Version| As a ... | I want to ... | So that I can ...| |--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +|v1.0|new user|see help instructions|refer to them when I forget how to use the application| +|v1.0|zoo clerk|add an animal entry|record the animals in the zoo, and refer to them afterwards| +|v1.0|zoo clerk|delete an animal entry|remove redundant or invalid animal entry| +|v1.0|zoo clerk|list all the entries|see what entries I have entered previously, and refer to them| +|v1.0|zoo clerk|have an exit command|close the program safely| +|v2.0|zoo clerk|find an entry item by name|locate the entry I want, without having to go through the entire list| +|v2.0|zoo clerk|edit a command|change the entry contents to reflect the changes in the zoo| +|v2.0|zoo clerk|see a summary of all entries|have an idea of the entries at a glance| +|v2.1|zoo clerk|export the entries|easily move my data from one place to another| + + +-------------------------------------------------------------------------------------------------------------------------------------- + +## Non-Functional Requirements 🐯 + +1. Should be portable and working on any mainstream OS as long as it has Java 11 or above installed. +2. Should be able to hold up to 1000 wildlife records without a noticeable drop in performance for typical usage. +3. The average user who is familiar with typing on a keyboard should accomplish their tasks faster with WildWatch than a typical GUI app + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Manual Testing 🐼 + +### 1. Launching the App +- Refer to the [Quick Start](#quick-start-) section to get WildWatch set up and running. + +### 2. Getting help +- Run the `help` command by typing `help` and hitting the `Enter` key on your keyboard. + +### 3. Adding a Wildlife Entry +- Run the following command `add D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/looks healthy`. +Expected: Wildlife recorded successfully added. + +- Run the following command `add D/02-03-2023`. +Expected: No new recorded added. + +### 4. Listing Wildlife Entries +- Run the following command `list`. +Expected: All wildlife records shown. + + +### 5. Editing a Wildlife Entry +- Run the following command `edit I/1 D/02-05-2020 R/looks sick`. +Expected: Successfully edited the wildlife record. + +- Run the following command `edit I/-1 D/02-05-2020 R/looks sick`. +Expected: Failed to edit as -1 is not a valid index. + + +### 6. Deleting a Wildlife Entry +- Run the following command `delete 1`. +Expected: Successfully deleted the first record. + +- Run the following command `delete 0`. +Expected: Failed to delete a record as 0 is an invalid record. + +[⬆ Back to top](#table-of-contents) + +
+ + +### 7. Getting a summary of the wildlife data +- Run the following command `summary`. +Expected: A summary of the wildlife record is displayed. + + +### 8. Exporting wildlife data to a local file +- Run the following command `export myobservations.csv`. +Expected: You will be prompted with further questions, on exporting your data. + + +### 9. Exiting the app +- Run the `bye` command. +Expected: WildWatch program terminates. + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Command Summary 🐻‍❄️ + +| Action | Format | +|---------------------------|--------------------------------------------------------------------| +| Getting Help | `help` | +| Adding Entries | `add D/ S/ N/ {R/}` | +| Adding Entries (interactive) | `add i/` | +| Deleting Entries | `delete ` | +| Finding Entries | `find ` +| Editing Entries | `edit I/ {D/} {S/} {N/} {R/}` | +| Summarizing Entries | `summary {}` | +| Listing Entries | `list` | +| Exporting Entries | `export {}` | +| Exit | `bye` | + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Glossary 🐨 +We are here to help you with terminologies used in the user guide, that may not be familiar to you. -## Non-Functional Requirements +| Terminology | Meaning | +| -------------- |------------------------------------------------------------| +| Command Line Interface | A way to communicate with your computer using texts. | +| Home Folder | Folder in which your program resides | -{Give non-functional requirements} +-------------------------------------------------------------------------------------------------------------------------------------- -## Glossary -* *glossary item* - Definition +## Acknowledgements 🐺 +Reference - [woodenclock](https://github.com/woodenclock/ip.git) +Reference - [AB3 Developer Guide](https://se-education.org/addressbook-level3/DeveloperGuide.html) +Meet the [people](https://ay2324s1-cs2113t-w11-2.github.io/tp/AboutUs.html) behind WildWatch! -## Instructions for manual testing +[⬆ Back to top](#table-of-contents) -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +-------------------------------------------------------------------------------------------------------------------------------------- diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..284efc3e12 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,14 @@ -# Duke +# WildWatch -{Give product intro here} +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/a44a2292-9ca1-4ab8-b585-782b0406d61c) +Welcome aboard fellow animal lover! We are really excited to have you here! 😉 +Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the Command Line Interface (CLI). +The purpose of this user guide is to familiarize you with the program and help you when you face a problem using it. +Its main job is to store and present animal data, this would make your job so much more convenient! + Useful links: -* [User Guide](UserGuide.md) -* [Developer Guide](DeveloperGuide.md) -* [About Us](AboutUs.md) +* [About Us](https://ay2324s1-cs2113t-w11-2.github.io/tp/AboutUs.html) +* [User Guide](https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html) +* [Developer Guide](https://ay2324s1-cs2113t-w11-2.github.io/tp/DeveloperGuide.html) + diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..60bfd67090 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,359 @@ -# User Guide +# WildWatch User Guide 🐘 -## Introduction +## Table of Contents +- [Introduction](#introduction-) + - [How to use the User Guide](#how-to-use-the-user-guide) + - [Legend](#legend) +- [Quick Start](#quick-start-) +- [Features](#features-) + - [Adding Entries](#1-adding-entries-add): `add` + - [Standard Mode](#11-standard-mode) + - [Interactive Mode](#12-interactive-mode-add-i) + - [Deleting Entries](#2-deleting-entries-delete): `delete` + - [Listing Entries](#3-listing-entries-list): `list` + - [Editing Entries](#4-editing-entries-edit): `edit` + - [Finding Entries](#5-finding-entries-find): `find` + - [Summarizing Entries](#6-summarizing-entries-summary): `summary` + - [Exporting Entries](#7-exporting-entries-export): `export` + - [Getting Help](#8-getting-help--help): `help` + - [Exiting](#9-exiting--bye): `bye` + - [Saving Entries](#10-saving-entries) +- [FAQ](#faq-) +- [Bug Reporting](#bug-reporting-) +- [Command Summary](#command-summary-) +- [Glossary](#glossary-) -{Give a product intro} + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Introduction 🐻 +Welcome aboard fellow animal lover! We are really excited to have you here! 😉 Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the [Command Line Interface (CLI)](#glossary-). The purpose of this user guide is to familiarize you with the program and help you when you face a problem using it. Its main job is to store and present animal data, this would make your job so much more convenient! -## Quick Start +### How to use the User Guide +- Are you new here? +No worries, head to the [Quick Start](#quick-start-) page, and you will be up and running in no time! +- Lost among the pages? +Head to the [Table of Contents](#table-of-contents) to look for the right pages. +- Need help with the functionalities? +Head to the [Features](#features-) page for detailed guidance. +- Do you have a question for us? +Head to the [FAQ](#faq-) page. +- Do you want a concise summary of all functionalities? +Head to the [Command Summary](#command-summary-) page for a summary of all commands. +- Not sure what that word meant? +Head to the [Glossary](#glossary-) page for its meaning. -{Give steps to get started quickly} +### Legend -1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +| Symbol | Meaning | +| ------------ | ------------ | +|❗ IMPORTANT | These are important instructions that you should follow, failure to do so may be fatal. | +| ✏ Note | These are important details that you should take note of, failure to do so may not be fatal. | +| ⬆ Back to top | Click to scroll back up to the `Table of Contents`. | +| 🐵 🦊 🦁 | Your cute animal friends pop up to let you know that you have reached a new section. | -## Features +[⬆ Back to top](#table-of-contents) + + +-------------------------------------------------------------------------------------------------------------------------------------- +
-{Give detailed description of each feature} +## Quick Start 🐵 -### Adding a todo: `todo` -Adds a new item to the list of todo items. +1. Ensure you have Java 11 or above installed in your Computer. (How do I install [Java 11](https://www.oracle.com/java/technologies/downloads/#java11)? What is my [Java version](https://www.java.com/en/download/help/version_manual.html)?) + +2. Download the latest `WildWatch.jar` from [here](https://github.com/AY2324S1-CS2113T-W11-2/tp/releases). -Format: `todo n/TODO_NAME d/DEADLINE` +3. Copy the file into the folder you want to use as the [home folder](#glossary-) for your WildWatch. -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +4. Open a [command terminal](https://www.freecodecamp.org/news/command-line-for-beginners/), `cd` into the home folder you put the jar file in, and run the following command: `java -jar WildWatch.jar`. +You should see the welcome screen as the diagram below. -Example of usage: +5. Type commands beside `>>>` and press `Enter` to execute it. +(e.g. typing `help` and pressing `Enter` will show the help page). -`todo n/Write the rest of the User Guide d/next week` +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/c110856f-dd16-4c5c-8205-446d86425ddb) -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +[⬆ Back to top](#table-of-contents) + + +-------------------------------------------------------------------------------------------------------------------------------------- +
-## FAQ +## Features 🦊 +> ❗ IMPORTANT +> * Capitalized words between angle brackets `< >` is a field to be filled up appropriately by the user. +> * E.g., `delete ` should be `delete 2` +> * Anything between curly brackets `{ }` is an optional field that may or may not be filled up by the user. +> * E.g., `summary {}` could be `summary lion` or `summary`. -**Q**: How do I transfer my data to another computer? + +### 1. Adding Entries: `add` +Adds a new wildlife entry to the WildWatch program. + +> ❗ IMPORTANT +> * `` has to be in the format **DD-MM-YYYY** +> * ``, ``, `` cannot be the same for 2 separate entries. + +> ✏ Note +> * `R/` here is optional, meaning it may be left as blank. + +#### 1.1 Standard Mode +Format: `add D/ S/ N/ {R/}` -**A**: {your answer here} +> ❗ IMPORTANT +> * The fields must be filled up in the following order: ``, ``, ``, ``. + +Example: +* `add D/02-03-2023 S/Annam Leaf Turtle N/Ariel` +* `add D/02-03-2023 S/Low Land Gorilla N/Strong One R/Aggressive` + +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/d548f580-3098-4d9d-8635-07e0a989e47c) -## Command Summary +
+ +#### 1.2 Interactive Mode: `add i/` +Did you forget the fields to in the `add` command? Fear not, we've got your back! +Simply type `add i/`, which triggers the interactive mode for the `add` command. +It will prompt you each step of the way, notifying you if a mandatory field is left blank, so you don't have to worry about getting it wrong! -{Give a 'cheat sheet' of commands here} +Format: `add i/` + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/b255477f-fc2c-437c-a954-5b4c71945c26) -* Add todo `todo n/TODO_NAME d/DEADLINE` +[⬆ Back to top](#table-of-contents) + + + +
+### 2. Deleting Entries: `delete` +Deletes an entry of the specified INDEX. +The index refers to the index number shown in the displayed [entry list](#3-listing-entries-list). + +Format: `delete ` + +>❗ IMPORTANT +> * Index must be a positive integer 1, 2, 3, ... +> * Deleted items are not recoverable + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/587e450e-9219-4a42-b005-2b52e8c93de2) + + + +### 3. Listing Entries: `list` +Shows a list of all entries of the wildlife in WildWatch so far. +Shows useful information about each entry. +These include information such as the date, species, name, and remarks. + +Format: `list` + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/ef21314f-fb23-4bd6-bcfd-fddfa8fb0330) + +[⬆ Back to top](#table-of-contents) + + + +
+### 4. Editing Entries: `edit` +Edits an entry in the program. + +Format: `edit I/ {D/} {S/} {N/} {R/}` + +>❗ IMPORTANT +> * `` has to be in the format **DD-MM-YYYY** +> * `I/` argument is required. +> * The fields must be filled up in the following order: ``, ``, ``, ``. + +> ✏ Note +> * `D/` `S/` `N/` `R/` fields are optional, but at least 1 needs to be filled up. + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/93d3f7ce-f714-4149-953d-703d6d58c671) + +[⬆ Back to top](#table-of-contents) + + + +
+### 5. Finding Entries: `find` +Finds all the entries that match the search word. + +Format: `find ` + +> ✏ Note +> * `` here could be the full-date (**DD-MM-YYYY**), or part of the name, the species, or the remark. + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/b77bd98a-6ea3-4378-aef2-bd436794b246) + + + +### 6. Summarizing Entries: `summary` +Shows a summary of all wildlife in the WildWatch system by the species type. + +Format: `summary {}` + +> ✏ Note +> * `` here is optional. +> * When specified, it will show a summary of the wildlife recorded for the specified species. + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/4a089c9f-ef0a-4c57-96a1-48479614ac9c) + +[⬆ Back to top](#table-of-contents) + + + +
+### 7. Exporting Entries: `export` +Do you need to share the entries you've collected with someone else? No worries, we have your back! We provide a convenient way for you to export your data as a [Comma Separated Values (CSV)](#glossary-) file. CSV files can be imported into universally-used tools like Microsoft Excel, allowing you to share your data and insights effortlessly. We also guide you through the process of selecting the columns that you want to include in your CSV, if you only want to share some parts of the data. This would come in really handy!👍 + +Format: `export {}` + +> ✏ Note +> * `` here is optional. +> * However, if you choose to include it, note that it should end with the file extension `.csv` so that it can be recognized as a CSV file. +> * If unspecified, your data will be written to the default file `WildWatch.csv`. + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/e423b685-2b61-4e90-aee0-821fc4c44659) + +[⬆ Back to top](#table-of-contents) + + + +
+### 8. Getting Help : `help` +The help page shows a brief overview on how to use all the commands. +Additionally, you can also specify a command to view the datailed help page. + +Format: `help {}` + +> ✏ Note +> * `` here is optional. +> * It may be one of the following: `add`, `delete`, `list`, `edit`, `find`, `summary`, `export`, `bye`, `full`. + +Example: +![image](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/69474977/86dff05d-057a-4989-bcc0-d234b9b1b3ce) + + + +### 9. Exiting : `bye` +To close and save the program. + +Format: `bye` + + + +### 10. Saving Entries + +Data of the entries are saved in the local storage automatically. +Now, you can use the WildWatch with a peace of mind! + +[⬆ Back to top](#table-of-contents) + + +-------------------------------------------------------------------------------------------------------------------------------------- + +## FAQ 🦁 + +> **Q**: Why do I get the `OOPS!!! Invalid Date input :-(` error? +> **A**: Ensure the day, month, year is separated by `-` e.g., `23-05-2023` in **DD-MM-YYYY** format. + + +> **Q**: Can I add photos or other media to the entries? +> **A**: Currently, WildWatch only supports text-based entries. You can add descriptions, but it does not support adding photos or media directly. + + +> **Q**: I accidentally deleted an entry. Is there a way to recover it? +> **A**: Unfortunately, deleted entries are not recoverable, so it's important to be careful when using the delete command. + + +> **Q**: I made a mistake while adding an entry, can I edit or correct it? +> **A**: Sure thing! You can edit an entry using the edit command. Simply specify the index of the entry you want to edit and provide the updated information. For example, you can use `edit I/1 D/02-03-2024` to edit the date of the first entry. + + +> **Q**: I found a bug! What do I do? +> **A**: Great! Please contact [Min](https://github.com/woodenclock), your help is greatly appreciated! Alternatively, you may refer to our [Bug Reporting](#bug-reporting-) section to report the bug yourself! + +[⬆ Back to top](#table-of-contents) + + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Bug Reporting 🐝 + +You can do the following steps below to report an issue with our program. +We value your feedback, and opening an issue on our GitHub repository is a great way to help us identify and address problems. + +### Step-by-Step Instructions: + +1. **Visit the GitHub Repository** + - In your web browser, navigate to our [GitHub repository](https://github.com/AY2324S1-CS2113T-W11-2/tp). + +2. **Sign in to GitHub** + - If you don't have a GitHub account, you'll need to create one. If you already have an account, sign in to GitHub. + +3. **Go to the `Issues` Tab** + - Once you're signed in, go to the "Issues" tab of our GitHub repository. It's usually located in the menu bar near the top of the page. + +4. **Open a `New Issue`** + - Click on the "New Issue" button to create a new issue. + +5. **Describe the Issue** + - In the issue creation page, you'll find a text box for the issue title and a larger text box for the issue description. Fill in the following details: + - Title: A concise and descriptive title for the issue. + - Description: Provide a detailed description of the issue. Include steps to reproduce the problem if applicable. + +6. **Add Labels and Milestones (if needed)** + - You can optionally add labels and milestones to categorize the issue. This can help us prioritize and address the issue more effectively. + +7. **Submit the Issue** + - Once you've filled in the issue title and description, click the "Submit new issue" button. Your issue will be created and submitted to the repository. + +### Additional Tips: +- Before creating a new issue, please check if the issue you want to report already exists in the repository's issue list. This can help avoid duplicate reports. + +Thank you for contributing to the improvement of our program. Your feedback is highly appreciated! 🙏 + +[⬆ Back to top](#table-of-contents) + + +-------------------------------------------------------------------------------------------------------------------------------------- +
+ +## Command Summary 🐯 + +| Action | Format | +|---------------------------|--------------------------------------------------------------------| +| Adding Entries | `add D/ S/ N/ {R/}` | +| Adding Entries (interactive) | `add i/` | +| Deleting Entries | `delete ` | +| Listing Entries | `list` | +| Finding Entries | `find ` +| Editing Entries | `edit I/ {D/} {S/} {N/} {R/}` | +| Summarizing Entries | `summary {}` | +| Exporting Entries | `export {}` | +| Getting Help | `help {}` | +| Exit | `bye` | + + +-------------------------------------------------------------------------------------------------------------------------------------- + +## Glossary 🐨 +We are here to help you with terminologies used in the user guide, that may not be familiar to you. + +| Terminology | Meaning | +|---------------|------------------------------------------------------------| +| Command Line Interface | A way to communicate with your computer using texts. | +| Home Folder | Folder in which your program resides | +| Comma-Separated Value | A file format that allows data to be saved in tabular format | + +[⬆ Back to top](#table-of-contents) + +-------------------------------------------------------------------------------------------------------------------------------------- diff --git a/docs/diagrams/AddSequenceDiagram.puml b/docs/diagrams/AddSequenceDiagram.puml new file mode 100644 index 0000000000..4aa856c955 --- /dev/null +++ b/docs/diagrams/AddSequenceDiagram.puml @@ -0,0 +1,57 @@ +@startuml +!include Style.puml + +box +participant "CommandHandler" <> +participant ":EntryHandler" as EntryHandler +participant ":AddCommandParser" as AddCommandParser +participant ":AddCommand" as AddCommand +end box + +-> CommandHandler : processCommand(inputBuffer) +activate CommandHandler +CommandHandler -> EntryHandler + +activate EntryHandler + +create AddCommandParser +EntryHandler -> AddCommandParser : AddCommandParser() +activate AddCommandParser + +AddCommandParser --> EntryHandler +deactivate AddCommandParser + +EntryHandler -> AddCommandParser : parse(input) +activate AddCommandParser + +AddCommandParser -> AddCommandParser : performChecks(input) +activate AddCommandParser + +deactivate AddCommandParser + +create AddCommand +AddCommandParser -> AddCommand : AddCommand(entry) +activate AddCommand + +AddCommand --> AddCommandParser : command +deactivate AddCommand + +AddCommandParser --> EntryHandler : command +deactivate AddCommandParser + +EntryHandler --> CommandHandler : command +deactivate EntryHandler + +CommandHandler -> CommandHandler : executeCommand(command) +activate CommandHandler + +CommandHandler -> AddCommand : execute() +activate AddCommand + +deactivate AddCommand +deactivate CommandHandler + +<-- CommandHandler +deactivate CommandHandler + +@enduml \ No newline at end of file diff --git a/docs/diagrams/DeleteCommand.puml b/docs/diagrams/DeleteCommand.puml new file mode 100644 index 0000000000..f34b9929e7 --- /dev/null +++ b/docs/diagrams/DeleteCommand.puml @@ -0,0 +1,45 @@ +@startuml +'https://plantuml.com/sequence-diagram + +autonumber + +title Delete Command + +participant a as "WildWatch" +participant b as "InputHandler" +participant c as "Ui" +participant d as "LOGGER" +participant e as "CommandHandler" +participant f as "EntryHandler" +participant g as "DeleteCommand" +participant h as "EntryList" + +a->b: handleInput() +b->c: inputPromptPrinter() +c-->b: return void +b->c: inputRetriever() +c-->b: return input +b->d: log(..) +d-->b: return void +b->e: processCommand(input) +deactivate b +deactivate c +e->f: handleEntry(input) +f->g: constructor DeleteCommand(..) +activate g +deactivate g +f-->e: return command +e->e: executeCommand(command) +e->c: printHorizontalLines() +c-->e: return void +e->g: execute() +activate g +g->c: prompt for confirmation +c-->g: user confirmation +g->h: deleteEntry(index) [if confirmed] +h-->g: return void [after deletion] +deactivate g +g-->e: return void +e->c: printHorizontalLines() +c-->e: return void +@enduml diff --git a/docs/diagrams/HelpCommand.puml b/docs/diagrams/HelpCommand.puml new file mode 100644 index 0000000000..bb9593447d --- /dev/null +++ b/docs/diagrams/HelpCommand.puml @@ -0,0 +1,38 @@ +@startuml +'https://plantuml.com/sequence-diagram + +autonumber + +title Help Command + +participant a as "WildWatch" +participant b as "InputHandler" +participant c as "Ui" +participant d as "LOGGER" +participant e as "CommandHandler" +participant f as "EntryHandler" +participant g as "HelpCommand" + +a->b: handleInput() +b->c: inputPromptPrinter() +c-->b: return void +b->c: inputRetriever() +c-->b: return input +b->d: log(..) +d-->b: return void +b->e: processCommand(input) +deactivate b +deactivate c +e->f: handleEntry(input) +f->g: constructor HelpCommand(..) +activate g +deactivate g +f-->e: return command +e->e: executeCommand(command) +e->c: printHorizontalLines() +c-->e: return void +e->g: execute() +g-->e: return void +e->c: printHorizontalLines() +c-->e: return void +@enduml \ No newline at end of file diff --git a/docs/diagrams/Style.puml b/docs/diagrams/Style.puml new file mode 100644 index 0000000000..56bf77beca --- /dev/null +++ b/docs/diagrams/Style.puml @@ -0,0 +1,14 @@ +@startuml +!define WILDWATCH_COLOR #006368 +!define MAIN_COLOR #FF0000 +!define PARSER_COLOR #ED7D31 +!define STORAGE_COLOR #FFC000 +!define COMMAND_COLOR #00B050 +!define ENTRY_COLOR #00B0F0 +!define ENTRYLIST_COLOR #4472C4 +!define UI_COLOR #7030A0 +!define LOCALSTORAGE_COLOR #000000 + +hide footbox + +@enduml \ No newline at end of file diff --git a/docs/images/AddSequenceDiagram.png b/docs/images/AddSequenceDiagram.png new file mode 100644 index 0000000000..71e32fcb1b Binary files /dev/null and b/docs/images/AddSequenceDiagram.png differ diff --git a/docs/images/person.png b/docs/images/person.png new file mode 100644 index 0000000000..f6ef06c0d3 Binary files /dev/null and b/docs/images/person.png differ diff --git a/docs/team/imaginarys96.md b/docs/team/imaginarys96.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index ab75b391b8..0000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,6 +0,0 @@ -# John Doe - Project Portfolio Page - -## Overview - - -### Summary of Contributions diff --git a/docs/team/lctxct.md b/docs/team/lctxct.md new file mode 100644 index 0000000000..a5ad9b3bbe --- /dev/null +++ b/docs/team/lctxct.md @@ -0,0 +1,44 @@ +# Cai Ting's Portfolio + +## Project: WildWatch +Welcome aboard fellow animal lover! We are really excited to have you here! 🐋 +Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the Command Line Interface (CLI). + +--- + +## Summary of contributions + +### Code contributed: [link](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=lctxct&tabRepo=AY2324S1-CS2113T-W11-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + +### Enhancements implemented: +* `add` command +* interactive `add` command +* `export` command +* `FileCommand`, which abstracts out the process of saving entries to files +* Helped to enhance skeleton with some abstract classes / interfaces: `Command`, `Parser` +* Extended existing `InvalidInputException` to allow for more robust error handling. Through overloading, it is possible to make use of `InvalidInputErrorType`s so that error messages common to multiple commands may share an error message, else it is also possible to define custom error messages. +* `AddCommandParserTest` + +### Contributions to team-based tasks +* Helped with refactoring and decoupling of initial code for increased maintainability and ease of contribution. This was not an easy task, as by the time I had started looking at the code many of the components were already cemented and quite complex. It took me a full weekend to both thoroughly understand how parts of the code linked together + figure out how best to decouple the components to ensure that I would be able to preserve parts of code already written by my teammates as much as possible, and would not heavily affect ongoing development. In the end, changes I made allowed for error-handling to be settled by-command, allowing for more fine-grained error messages and hence a better user experience. +* Created issues for v2.0 so that there'll be less chance of overlapping efforts and everyone has the chance to contribute by assigning themselves to an issue. +* Consolidated PE-D issues into one place, noting duplicate problems, so that it's easier to track and resolve issues (e.g. [link1](https://github.com/AY2324S1-CS2113T-W11-2/tp/issues/181), [link2](https://github.com/AY2324S1-CS2113T-W11-2/tp/issues/191)). + +### Review/mentoring contributions +* Helped with testing and created issues for bugs found (e.g. [link](https://github.com/AY2324S1-CS2113T-W11-2/tp/issues/115)) + +### Contributions beyond the project team +* Participated in the code review of other team's DG in tutorial +* Helped to spot issues in other team's code during PE-D + +### Contributions to the UG +* Created initial markdown skeleton +* Created initial command reference table +* `add` command +* `export` command +* Helped to review the UG + +### Contributions to the DG +* Contributed `AddSequenceDiagram.puml` +![AddSequenceDiagram](https://github.com/AY2324S1-CS2113T-W11-2/tp/assets/70379887/c3beaaca-ff23-4d09-95b1-30bd88b5997c) + diff --git a/docs/team/tangzhenen.md b/docs/team/tangzhenen.md new file mode 100644 index 0000000000..8c61eb54c9 --- /dev/null +++ b/docs/team/tangzhenen.md @@ -0,0 +1,29 @@ +# Tang Zhen En's Project Portfolio Page + +## Overview +Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the Command Line Interface (CLI). +Its main job is to store and present animal data in an easy and convenient way making your job so much more convenient! + +-------------------------------------------------------------------------------------------------------------------------------------- + +## Summary of Contributions + +### Code Contributed +To view the codes that I have contributed to the team project WildWatch, click [RepoSense](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=tangzhenen&breakdown=false&sort=groupTitle%20dsc&sortWithin=title&since=2023-09-22&timeframe=commit&mergegroup=&groupSelect=groupByRepos). + +### Enhancements Implemented +1. Created `delete` command class. +2. Updated `help` command class. +3. Created tests for `delete`,`list` and `help` commands. + +### Contributions to User Guide +1. Created the `delete` feature section. +2. Created the `bye` feature section. +3. Created the `bug reporting` section to help users deal with issues when using the program. +4. Updated `FAQ` section. +5. Helped to review the UG. + + +### Contributions beyond the project team +1. Participated in reviewing other team's DG during tutorial. +2. Participated in PE-D to review other team's code and reported bugs found. \ No newline at end of file diff --git a/docs/team/woodenclock.md b/docs/team/woodenclock.md new file mode 100644 index 0000000000..441b0db3ab --- /dev/null +++ b/docs/team/woodenclock.md @@ -0,0 +1,55 @@ +# Lee Sungmin's Project Portfolio Page 🦈 + +## Overview +Wildwatch is a program for a clerk managing wildlife data in a wildlife reserve via the Command Line Interface (CLI). +Its main job is to store and present animal data, this would make your job so much more convenient! + +-------------------------------------------------------------------------------------------------------------------------------------- + +## Summary of Contributions + +### Code Contributed +To view the codes that I have contributed to the team project WildWatch, click [RepoSense](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=woodenclock&breakdown=true). + +### Enhancements Implemented +1. Organizing all class files into their repective packages (e.g., `ui`, `execute`, etc). +2. Created `List`, `Find`, and `Bye` classes in `Command Package`. +3. Enhanced `Export`, `Summary`, `Help` classes in `Command Package` to be more robust and user-friendly. +4. Created the `Entry` and `EntryList` classes in `Entry Package`. +5. Created `DateChecker` and `ErrorHandler` (skeleton was enhanced by [Cai Ting](https://ay2324s1-cs2113t-w11-2.github.io/tp/team/lctxct.html)) classes in Error Package. +6. Created `InputHandler` and `CommandHandler` classes in `Execute Package`. +7. Created `BootUp`, `ShutDown`, and `LogHandler` classes in `Miscellaneous Package`. +8. Created `EditCommandParser`, `ListCommandParser` and `FindCommandParser` classes in `Parser Package`. +9. Enhanced `SummaryCommandParser` class in `Parser Package` to be case-insensitive. +10. Created `ExistenceChecker`, `FileCreater`, `FileHandler`, `FileLoader`, `Saver`, and `EntryToStingConverter` (half) classes in `Storage Package`. +11. Created `EmptyDescriptionPrinter`, `EntryPrinter`, `ErrorPrinter`, `FilePrinter`, `InputConsole`, `LinePrinter`, `ListCommandPrinter`, `SearchResultPrinter` classes in `Ui Package`. +12. Created half of the all test classes, please refer to [RepoSense](https://nus-cs2113-ay2324s1.github.io/tp-dashboard/?search=woodenclock&breakdown=true). +13. Updated README.md page from Duke to WildWatch appropriately. + +### Contributions to User Guide +1. Created `Table of Contents` Section, ensured all links are working. +2. Created `Intoduction` Section, which includes Welcome Message, Description, How to use UG, and Legend. +3. Created `Quick Start` Section, which guides new comers on how to get WildWatch up and running. +4. Created `Listing Entries` Sub-Section. +5. Created `FAQ` Section. +6. Created `Glossary` Section. + +### Contributions to Developer Guide +1. Created `Table of Contents` Section, ensured all links are working. +2. Created `Intoduction` Section, which includes Welcome Message, Description, Purpose, Audience, and Legend. +3. Created `Quick Start` Section, which guides new comers on how to get WildWatch up and running. +4. Created `Design & Implementation` Section and their respective descriptions (did everything except Command Sequence Diagram). +5. Created `Product Scope` Section, which described the `Target User Profile` and `Value Proposition`. +6. Created `Glossary` and `Acknowledgement` Sections. + +### Contributions to Team-Based Tasks +1. Set up GitHub team organization and upstream repository. +2. Set up foundational codes, on which WildWatch first got up and running. +3. Set up and maintained `Issue Tracker`, gave constructive feedbacks to Pull Requests, reconciled merge conflicts. +4. Managed all creation and release of `WildWatch.jar` artefacts. +5. Managed all conversions to pdf, and submission. +6. Soley created and editted the `Demo Video`. + +### Contributions in Reviews/Mentoring +1. Constructive comments and reviews were given in the [`Pull Request`](https://github.com/AY2324S1-CS2113T-W11-2/tp/pulls?page=3&q=is%3Apr+is%3Aclosed). +2. When giving the feedbacks, [visual aids](https://github.com/AY2324S1-CS2113T-W11-2/tp/pull/1) were also attached, to better facilitate the message sent across. diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d59..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/main/java/seedu/wildwatch/WildWatch.java b/src/main/java/seedu/wildwatch/WildWatch.java new file mode 100644 index 0000000000..b51b912ecd --- /dev/null +++ b/src/main/java/seedu/wildwatch/WildWatch.java @@ -0,0 +1,19 @@ +package seedu.wildwatch; + +import seedu.wildwatch.miscellaneous.LogHandler; +import seedu.wildwatch.miscellaneous.BootUp; +import seedu.wildwatch.storage.FileHandler; +import seedu.wildwatch.execute.InputHandler; + +public class WildWatch { + /** + * Main entry-point for the java.wildwatch.WildWatch application. + */ + public static void main(String[] args) { + LogHandler.configure(); //Configure Log + BootUp.bootUpOne(); + FileHandler.handleFile(); //FileHandler takes on + InputHandler.handleInput(); + + } +} diff --git a/src/main/java/seedu/wildwatch/command/AddCommand.java b/src/main/java/seedu/wildwatch/command/AddCommand.java new file mode 100644 index 0000000000..b6cf897a26 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/AddCommand.java @@ -0,0 +1,65 @@ +//@@lctxct +package seedu.wildwatch.command; + +import java.util.regex.Pattern; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.ui.AddCommandPrinter; +import seedu.wildwatch.ui.EntryPrinter; +import seedu.wildwatch.ui.ListCommandPrinter; + +/** + * Command class for adding entry to EntryList + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final Pattern ADD_DEFAULT_COMMAND_FORMAT = + Pattern.compile("add" + + "\\s* D/\\s*(?[^/]+)" + + "\\s* S/\\s*(?[^/]+)" + + "\\s* N/\\s*(?[^/]+)" + + "\\s*(?: R/\\s*(?.*)\\s*)?"); + + public static final Pattern ADD_INTERACTIVE_COMMAND_FORMAT = + Pattern.compile("add\\s+i/\\s*"); + + private final Entry newEntry; + + public AddCommand(Entry entry) { + newEntry = entry; + } + /** + * Add a new Entry + */ + @Override + public void execute() { + EntryList.addEntry(newEntry); + + AddCommandPrinter.entryAddedMessagePrinter(); + EntryPrinter.printEntry(EntryList.getArraySize()-1); + ListCommandPrinter.entryCountPrinter(); + } + /** + * Override the default == operator to compare objects. + * This is used to compare two AddCommand, to determine if the Entry that + * that is being added are duplicates + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddCommand)) { + return false; + } + + AddCommand otherAddCommand = (AddCommand) other; + return newEntry.equals(otherAddCommand.newEntry); + } +} + diff --git a/src/main/java/seedu/wildwatch/command/AddFileStringCommand.java b/src/main/java/seedu/wildwatch/command/AddFileStringCommand.java new file mode 100644 index 0000000000..f890993a15 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/AddFileStringCommand.java @@ -0,0 +1,28 @@ +//@@lctxct +package seedu.wildwatch.command; + +import java.util.regex.Pattern; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; + +public class AddFileStringCommand extends Command { + public static final Pattern FILE_STRING_FORMAT = + Pattern.compile("\\s*(?[^/]+)\\s*/" + + "\\s*(?[^/]+)\\s*/" + + "\\s*(?[^/]+)\\s*/" + + "\\s*(?[^/]+)"); + + private final Entry newEntry; + + public AddFileStringCommand(Entry entry) { + newEntry = entry; + } + /** + * Add new a Entry + */ + @Override + public void execute() { + EntryList.addEntry(newEntry); + } +} diff --git a/src/main/java/seedu/wildwatch/command/ByeCommand.java b/src/main/java/seedu/wildwatch/command/ByeCommand.java new file mode 100644 index 0000000000..739f80dd62 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/ByeCommand.java @@ -0,0 +1,24 @@ +package seedu.wildwatch.command; + +import seedu.wildwatch.miscellaneous.ShutDown; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ByeCommand extends Command { + + public static final String COMMAND_WORD = "bye"; + + private static final Logger LOGGER = Logger.getLogger(ByeCommand.class.getName()); + /** + * Exit the program + */ + public static void exitProgram() { + LOGGER.log(Level.INFO, "Initiating shutdown procedures."); + ShutDown.shutDown(); + } + + //TODO: figure out a way to get rid of this + public void execute() { + } +} diff --git a/src/main/java/seedu/wildwatch/command/Command.java b/src/main/java/seedu/wildwatch/command/Command.java new file mode 100644 index 0000000000..332628f900 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/Command.java @@ -0,0 +1,12 @@ +package seedu.wildwatch.command; + +import seedu.wildwatch.exception.InvalidInputException; + +/** + * Command class for manipulating EntryList + */ +public abstract class Command { + public Command() {} + + public abstract void execute() throws InvalidInputException; +} diff --git a/src/main/java/seedu/wildwatch/command/DeleteCommand.java b/src/main/java/seedu/wildwatch/command/DeleteCommand.java new file mode 100644 index 0000000000..278d8f30c5 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/DeleteCommand.java @@ -0,0 +1,66 @@ +package seedu.wildwatch.command; + +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.miscellaneous.ShutDown; +import seedu.wildwatch.ui.LinePrinter; +import seedu.wildwatch.ui.EntryPrinter; +import seedu.wildwatch.ui.ListCommandPrinter; +import seedu.wildwatch.ui.DeleteCommandPrinter; + +import java.util.Scanner; + +/** + * Command class for deleting entry in EntryList + */ +public class DeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + private final int numberInput; + + public DeleteCommand(int numberInput) { + this.numberInput = numberInput; + } + + /** + * Deletes entry in the EntryList + */ + public void execute() { + if (numberInput <= 0 || numberInput > EntryList.getArraySize()) { + DeleteCommandPrinter.entryNotFoundMessagePrinter(); + } else { + confirmDelete(); + } + } + + /** + * Confirm if entry should be deleted + */ + public void confirmDelete() { + Scanner scanner = new Scanner(System.in); + + for (int i = 0; i < 5; i++) { + EntryPrinter.printEntry(numberInput-1); + DeleteCommandPrinter.entryDeletionConfirmationPrinter(i); + String confirmation = scanner.nextLine().trim().toLowerCase(); + + if (confirmation.equals("yes")) { + DeleteCommandPrinter.entryRemovedMessagePrinter(); + EntryList.deleteEntry(numberInput-1); + ListCommandPrinter.entryCountPrinter(); + return; + } + if (confirmation.equals("no")) { + LinePrinter.printHorizontalLines(); + DeleteCommandPrinter.entryNotDeletedMessagePrinter(); + return; + } + if (confirmation.equals("bye")) { + ShutDown.shutDown(); + System.exit(0); + } + } + LinePrinter.printHorizontalLines(); + DeleteCommandPrinter.entryNotDeletedMessagePrinter(); + } +} diff --git a/src/main/java/seedu/wildwatch/command/EditCommand.java b/src/main/java/seedu/wildwatch/command/EditCommand.java new file mode 100644 index 0000000000..26d0b6e98e --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/EditCommand.java @@ -0,0 +1,136 @@ +package seedu.wildwatch.command; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.ui.EditCommandPrinter; +import seedu.wildwatch.ui.EntryPrinter; +import seedu.wildwatch.error.InvalidInputErrorType; + +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EditCommand extends Command { + public static final String COMMAND_WORD = "edit"; + + public static final Pattern EDIT_ENTRY_COMMAND_FORMAT = + Pattern.compile("edit" + + " I/(?[^/]+)" + + "( D/(?[^/]+))?" + + "( S/(?[^/]+))?" + + "( N/(?[^/]+))?" + + "(?: R/(?[^/]*))?"); + private String input; + + public EditCommand(String input) { + this.input = input; + } + + /** + * Returns the updated entry, that contains information as stated in edit command input. + * + * @param entry + * @param date + * @param species + * @param name + * @param remark + * @param index + * @return Entry + * @throws InvalidInputException + */ + public Entry checkAndUpdateEntry(Entry entry, String date, String species, String name, String remark, int index) + throws InvalidInputException { + // Check for duplicate entry + if (checkDuplicateEntry(entry, date, species, name, index)) { + throw new InvalidInputException("Edit produces a duplicate entry!"); + } + + if( date != null && !date.isEmpty() ) { + date = date.trim(); + entry.setDate(date); + } + if( species != null && !species.isEmpty() ) { + species = species.trim(); + entry.setSpecies(species); + } + if( name != null && !name.isEmpty() ) { + name = name.trim(); + entry.setName(name); + } + if( remark != null && !remark.isEmpty() ) { + remark = remark.trim(); + entry.setRemark(remark); + } + return entry; + } + + /** + * Executes the edit command. + * Matches the input with the command format. + * Checks if the index input is valid. + * + * @throws InvalidInputException + */ + public void execute() throws InvalidInputException { + final Matcher matcher = EDIT_ENTRY_COMMAND_FORMAT.matcher(input); + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + final String indexStr = matcher.group("index").trim(); + final String date = matcher.group("date"); + final String species = matcher.group("species"); + final String name = matcher.group("name"); + final String remark = matcher.group("remark"); + int index = Integer.parseInt(indexStr); + if( index <= 0 || index > EntryList.getArraySize()) { + throw new InvalidInputException(InvalidInputErrorType.ENTRY_NOT_FOUND); + } + index -= 1; // EntryList is 0 based. + //System.out.println(indexStr + " " + date + " " + species + " " + name + " " + remark); + Entry currentEntry = EntryList.getEntry(index); + Entry updatedEntry = checkAndUpdateEntry(currentEntry, date, species, name, remark, index); + EntryList.editEntry(index, updatedEntry); + + EditCommandPrinter.entryEditedMessagePrinter(); + EntryPrinter.printEntry(index); + } + + /** + * Returns a boolean value on whether there is already an entry of the same day, species, name. + * + * @param entry + * @param date + * @param species + * @param name + * @param index + * @return + */ + private boolean checkDuplicateEntry(Entry entry, String date, String species, String name, int index) { + String newDate = date; + String newName = name; + String newSpecies = species; + + if (date == null || date.isEmpty()) { + newDate = entry.getDate().format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); + } + + if (name == null || name.isEmpty()) { + newName = entry.getName(); + } + + if (species == null || species.isEmpty()) { + newSpecies = entry.getSpecies(); + } + + Entry newEntry = new Entry(newDate, newSpecies, newName, ""); + + int duplicateEntryIndex = EntryList.checkEntryExists(newEntry); + if (duplicateEntryIndex != -1 && duplicateEntryIndex != index+1) { + // Entry is a duplicate + return true; + } + return false; + } +} diff --git a/src/main/java/seedu/wildwatch/command/ExportCommand.java b/src/main/java/seedu/wildwatch/command/ExportCommand.java new file mode 100644 index 0000000000..772262f6b6 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/ExportCommand.java @@ -0,0 +1,226 @@ +package seedu.wildwatch.command; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.error.FilenameChecker; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.storage.EntryToStringConverter; +import seedu.wildwatch.storage.ExistenceChecker; +import seedu.wildwatch.storage.FileCreator; +import seedu.wildwatch.ui.FilePrinter; +import seedu.wildwatch.ui.LinePrinter; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Scanner; +import java.util.regex.Pattern; + +/** + * Command class for exporting all current entries as a CSV file. + */ +public class ExportCommand extends Command { + + public static final String COMMAND_WORD = "export"; + + public static final Pattern EXPORT_COMMAND_FORMAT = + Pattern.compile("export(?:\\s+(?\\S+))?\\s*"); + + public static final String DEFAULT_FILENAME = "WildWatch.csv"; + + private static final String[] ALL_COLUMNS = new String[] { "date", "species", "name", "remark" }; + + private String filename; + + /** + * Constructs a new {@code ExportCommand}. + * + * @param file Name of file to set on {@code filename}. + */ + public ExportCommand(String file) { + filename = file; + } + + /** + * Constructs a new {@code ExportCommand}, with filename set + * to {@code DEFAULT_FILENAME}. + */ + public ExportCommand() { + this(DEFAULT_FILENAME); + } + + @Override + public void execute() throws InvalidInputException { + if (EntryList.isArrayEmpty()) { + throw new InvalidInputException("No entries to write to csv."); + } + + boolean fileExists = ExistenceChecker.checkFileExistence(filename); + + if (fileExists) { + FilePrinter.fileExistMessagePrinter(); + boolean canReplaceFile = canReplaceFile(); + createFileAccordingly(canReplaceFile); + } else { //File does not exist + FilePrinter.noFileMessagePrinter(); + System.out.print(System.lineSeparator()); + createFile(); + } + + ArrayList entries = EntryList.getArray(); + + ArrayList columnsToInclude = getColumnsToInclude(); + if (columnsToInclude.isEmpty()) { + throw new InvalidInputException("You need to select at least one column to include in the csv."); + } + + String header = "id"; + for (String column : columnsToInclude) { + header = String.join(",", header, column); + } + + try { + FileWriter writer = new FileWriter(filename); + + writer.write(header + "\n"); + for (int i = 0; i < EntryList.getArraySize(); i++) { + writer.write(EntryToStringConverter.toCsvString(entries.get(i), i+1, columnsToInclude)); + } + writer.close(); + } catch (IOException e) { + throw new InvalidInputException("Error writing to file."); + } + + FilePrinter.updateFileMessagePrinter(); + FilePrinter.csvCreationSuccess(filename); + } + + private void createFile() throws InvalidInputException { + try { + FileCreator.createFile(filename); + } catch (IOException e) { + throw new InvalidInputException("Unable to create file."); + } + } + + private void createFileAccordingly(boolean canReplaceFile) throws InvalidInputException { + if (!canReplaceFile) { + getNewFilename(); + createFile(); + } else { + new File(filename).delete(); + createFile(); + } + } + + + private void getNewFilename() throws InvalidInputException { + File file; + boolean fileExists = false; + do { + filename = setNewFilename(); + LinePrinter.printHorizontalLines(); + file = new File(filename); + fileExists = file.exists(); + if (fileExists) { + FilePrinter.fileAlreadyExistsMessagePrinter(); + System.out.print(System.lineSeparator()); + } + } while (fileExists); + } + + private String setNewFilename() throws InvalidInputException { + String newFilename; + Scanner scanner = new Scanner(System.in); + + boolean isValidFilename = false; + do { + FilePrinter.newFilenamePromptPrinterOne(); + FilePrinter.newFilenamePromptPrinterTwo(); + LinePrinter.printHorizontalLines(); + + newFilename = scanner.nextLine().trim(); + + if (newFilename.equals("q/")) { + LinePrinter.printHorizontalLines(); + throw new InvalidInputException("Exiting export command..."); + } + + isValidFilename = FilenameChecker.isValidCsvFilenameChecker(newFilename); + if (!isValidFilename) { + FilePrinter.invalidFilenameMessagePrinter(); + } + } while (!isValidFilename); + + return newFilename; + } + + /** + * Confirms with user if file can be replaced. Returns true if user + * accepts, and false otherwise. + * + * @return True if user allows file to be replaced, else false. + */ + private boolean canReplaceFile() { + Scanner scanner = new Scanner(System.in); + final String confirmationMessage = + String.format("%s already exists. Would you like to replace it? (Y/N)", filename); + + return doesUserApprove(scanner, confirmationMessage); + } + + /** + * Reads user input in a loop until "Y" or "N" received. + * + * @param scanner Scanner used to read user input + * @param confirmationMessage Message to prompt user for input + * @return true if user inputs "Y", false if user inputs "N" + */ + private boolean doesUserApprove(Scanner scanner, String confirmationMessage) { + do { + System.out.print(confirmationMessage); + System.out.print(": "); + String confirmation = scanner.nextLine().trim(); + + switch (confirmation.toLowerCase()) { + case ("y"): + LinePrinter.printHorizontalLines(); + return true; + case "n": + LinePrinter.printHorizontalLines(); + return false; + default: + LinePrinter.printHorizontalLines(); + FilePrinter.unrecognizedInputMessagePrinter(); + } + } while (true); + } + + private ArrayList getColumnsToInclude() { + ArrayList columnsToInclude = new ArrayList<>(); + + Scanner scanner = new Scanner(System.in); + + String includeAllColumnsMessage = "Would you like to include all columns? (Y/N)"; + if (doesUserApprove(scanner, includeAllColumnsMessage)) { + Collections.addAll(columnsToInclude, ALL_COLUMNS); + + return columnsToInclude; + } + + FilePrinter.selectColumnMessagePrinter(); + + for (String column : ALL_COLUMNS) { + String confirmationMessage = String.format("Would you like to include %s in your csv? (Y/N)", column); + + boolean shouldIncludeColumn = doesUserApprove(scanner, confirmationMessage); + if (shouldIncludeColumn) { + columnsToInclude.add(column); + } + } + + return columnsToInclude; + } +} diff --git a/src/main/java/seedu/wildwatch/command/FindCommand.java b/src/main/java/seedu/wildwatch/command/FindCommand.java new file mode 100644 index 0000000000..6733725875 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/FindCommand.java @@ -0,0 +1,78 @@ +//@@woodenclock +package seedu.wildwatch.command; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.ui.SearchResultPrinter; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Objects; + +/** + * Handles the "find" command to search for tasks that contain a specific keyword. + */ +public class FindCommand extends Command { + public static final String COMMAND_WORD = "find"; + private String input; + + public FindCommand(String input) { + this.input = input; + } + + /** + * Searches for tasks that contain the specified keyword and prints them. + */ + public void execute() throws InvalidInputException { + boolean hasMatch = false; + ArrayList entries = EntryList.getArray(); + ArrayList matchingEntries = new ArrayList<>(); + + LocalDate inputDate = null; + try { + //Is the user finding a date? + inputDate = LocalDate.parse(input, DateTimeFormatter.ofPattern("dd-MM-yyyy")); + } catch (DateTimeParseException exception) { + // matchingWord is not a date + } + + for (Entry entry: entries) { + boolean isInputADate = (inputDate != null && entry.getDate().equals(inputDate)); + + if (entry.getSpecies().contains(input) + || entry.getSpecies().toLowerCase().contains(input) + || entry.getName().contains(input) + || entry.getName().toLowerCase().contains(input) + || entry.getRemark().contains(input) + || entry.getRemark().toLowerCase().contains(input) + || isInputADate) { + hasMatch = true; + matchingEntries.add(entries.indexOf(entry)); + } + } + SearchResultPrinter.findEntryMessagePrinter(hasMatch, matchingEntries); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FindCommand)) { + return false; + } + + FindCommand otherFindCommand = (FindCommand) other; + return Objects.equals(this.input, otherFindCommand.input); + } + + @Override + public int hashCode() { + return Objects.hash(input); + } +} diff --git a/src/main/java/seedu/wildwatch/command/HelpCommand.java b/src/main/java/seedu/wildwatch/command/HelpCommand.java new file mode 100644 index 0000000000..850b45225f --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/HelpCommand.java @@ -0,0 +1,226 @@ +package seedu.wildwatch.command; + +public class HelpCommand extends Command { + + public static final String COMMAND_WORD = "help"; + + /** + * Requires UPDATE + */ + private static final String helpCommandMessage = + "_________________________________________________________________\n" + + "1. Get help - shows the list of commands available for WildWatch\n" + + " Format: help\n" + + " Example: \n" + + " help\n" + + "_________________________________________________________________\n"; + private static final String addCommandMessage = + "_________________________________________________________________\n" + + "2. To add a new wildlife\n" + + " Standard Mode:\n" + + " Format: add D/ S/ N/ {R/}\n" + + " DATE should be in the format DD-MM-YYYY\n" + + " Examples: \n" + + " add D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/Injured left flipper\n\n" + + " Interactive Mode:\n" + + " Format: add i/\n" + + " Example: \n" + + " add i/\n" + + "_________________________________________________________________\n"; + + private static final String listCommandMessage = + "_________________________________________________________________\n" + + "3. To list all wildlife entries,\n" + + " Format: list\n" + + " Example: \n" + + " list\n" + + "_________________________________________________________________\n"; + + private static final String deleteCommandMessage = + "_________________________________________________________________\n" + + "4. To delete a wildlife entry\n" + + " Format: delete \n" + + " The index refers to the index number shown in the displayed observation list.\n" + + " Note:\n" + + " - The index must be a positive integer:\n" + + " - Deleted items are not recoverable:\n" + + " Example: \n" + + " delete 1\n" + + "_________________________________________________________________\n"; + + + private static final String editCommandMessage = + "_________________________________________________________________\n" + + "5. To edit a wildlife entry\n" + + " Format: edit I/ {D/} {S/} {N/} {R/}\n" + + " The index refers to the index number shown in the displayed observation list.\n" + + " Note:\n" + + " - The index must be a positive integer:\n" + + " - DATE, SPECIES, NAME, REMARKS arguments are optional, but at least 1 is required.\n" + + " Example: \n" + + " edit I/1 D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/Injured left flipper\n" + + "_________________________________________________________________\n"; + + private static final String summaryCommandMessage = + "_________________________________________________________________\n" + + "6. To show a summary\n" + + " Format: summary {}\n" + + " Note:\n" + + " - argument is optional\n" + + " Examples: \n" + + " summary \n" + + " summary Annam Leaf Turtle\n" + + "_________________________________________________________________\n"; + + private static final String exportCommandMessage = + "_________________________________________________________________\n" + + "7. To export wildlife data\n" + + " Format: export {}\n" + + " Note:\n" + + " - argument is optional\n" + + " - argument if specified should end with a \".csv\" extension\n" + + " Examples: \n" + + " export \n" + + " export WildOnes.csv\n" + + "_________________________________________________________________\n"; + + private static final String findCommandMessage = + "_________________________________________________________________\n" + + "8. To find wildlife entries\n" + + " Format: find \n" + + " Note:\n" + + " - SEARCH argument could contain the full date, or part of species, name, or remarks.\n" + + " Example: \n" + + " find Annam Leaf Turtle\n" + + "_________________________________________________________________\n"; + + private static final String byeCommandMessage = + "_________________________________________________________________\n" + + "9. To exit the program\n" + + " Format: bye\n" + + " Example: \n" + + " bye \n" + + "_________________________________________________________________\n"; + + private static final String helpPage = + "---------------------------HELP PAGE-----------------------------\n" + + helpCommandMessage + + addCommandMessage + + listCommandMessage + + deleteCommandMessage + + editCommandMessage + + summaryCommandMessage + + exportCommandMessage + + findCommandMessage + + byeCommandMessage + + "\n" + + "-------------------------HELP PAGE END---------------------------\n"; + + private static final String helpPageShort = "\n" + + "---------------------------HELP PAGE (short)-----------------------------\n" + + "Adding Entries: add D/ S/ N/ {R/}\n" + + "Adding Entries (Interactive): add i/\n" + + "Deleting Entries: delete INDEX\n" + + "Listing Entries: list\n" + + "Finding Entries: find \n" + + "Editing Entries: edit I/ {D/} {S/} {N/} {R/}\n" + + "Summarizing Entries: summary {}\n" + + "Exporting Entries: export {}\n" + + "Exit: bye\n" + + "See our User Guide: https://ay2324s1-cs2113t-w11-2.github.io/tp/UserGuide.html" + + "\n" + + "-------------------------HELP PAGE (short) END---------------------------\n"; + + + private static final String needHelpPage = + "Please type \"help\" if you need help."; + + private String input; + + public HelpCommand(String input) { + this.input = input; + } + + public static void printNeedHelpMessage() { + System.out.println(needHelpPage); + } + + /** + * Print specific help message based on the command specified + * + * @return String helpPage + */ + public static String getHelpPage() { + return helpPage; + } + + /** + * Print specific help message based on the command specified + * + * @param command command + */ + public void printMessageForCommand(String command) { + switch (command) { + case "add": + System.out.println("No worries, I'm here to help!"); + System.out.println(addCommandMessage); + break; + case "list": + System.out.println("No worries, I'm here to help!"); + System.out.println(listCommandMessage); + break; + case "delete": + System.out.println("No worries, I'm here to help!"); + System.out.println(deleteCommandMessage); + break; + case "edit": + System.out.println("No worries, I'm here to help!"); + System.out.println(editCommandMessage); + break; + case "summary": + System.out.println("No worries, I'm here to help!"); + System.out.println(summaryCommandMessage); + break; + case "export": + System.out.println("No worries, I'm here to help!"); + System.out.println(exportCommandMessage); + break; + case "find": + System.out.println("No worries, I'm here to help!"); + System.out.println(findCommandMessage); + break; + case "bye": + System.out.println("No worries, I'm here to help!"); + System.out.println(byeCommandMessage); + break; + case "full": + System.out.println("No worries, I'm here to help!"); + System.out.println(helpPage); + break; + default: + System.out.println("Oops, no such help command exists! :-("); + break; + } + } + + /** + * Prints out help page + */ + public void execute() { + if (input.isEmpty()) { + System.out.println("No worries, I'm here to help!"); + System.out.println(helpPage); + return; + } + + String argument = input.replace("help", "").trim(); + if (argument.isEmpty()) { + // print short version + System.out.println("No worries, I'm here to help!"); + System.out.println(helpPageShort); + } else { + // e.g `help add` + printMessageForCommand(argument); + } + } +} diff --git a/src/main/java/seedu/wildwatch/command/ListCommand.java b/src/main/java/seedu/wildwatch/command/ListCommand.java new file mode 100644 index 0000000000..a3553e8389 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/ListCommand.java @@ -0,0 +1,40 @@ +//@@woodenclock +package seedu.wildwatch.command; + +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.ui.EntryPrinter; +import seedu.wildwatch.ui.ListCommandPrinter; +import seedu.wildwatch.error.InvalidInputErrorType; + +/** + * Command class for listing all entries in EntryList + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + /** + * Lists out all entry in EntryList + */ + public static void listEntry() { + int arraySize = EntryList.getArraySize(); + for (int i = 0; i < arraySize; i++) { + System.out.print(i + 1 + "."); + EntryPrinter.printEntry(i); + } + ListCommandPrinter.entryCountPrinter(); + } + /** + * Print the list of Entry + */ + public void execute() throws InvalidInputException { + if (EntryList.isArrayEmpty()) { + throw new InvalidInputException(InvalidInputErrorType.EMPTY_LIST); + } + + ListCommandPrinter.listMessagePrinter(); + listEntry(); + } +} + diff --git a/src/main/java/seedu/wildwatch/command/SummaryCommand.java b/src/main/java/seedu/wildwatch/command/SummaryCommand.java new file mode 100644 index 0000000000..f3147a68c2 --- /dev/null +++ b/src/main/java/seedu/wildwatch/command/SummaryCommand.java @@ -0,0 +1,98 @@ +package seedu.wildwatch.command; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.ui.SummaryCommandPrinter; + +/** + * Command class for `summary` + */ +public class SummaryCommand extends Command { + + public static final String COMMAND_WORD = "summary"; + + private String speciesName; + + public SummaryCommand(String speciesName) { + this.speciesName = speciesName.toLowerCase(); + } + + /** + * Group the wildlife entries by their species + * + * @param entries + * @return A map of the wildlife entries grouped by their species + */ + private static Map> groupEntriesBySpecies(List entries) { + + Map> map = new HashMap>(); + for (Entry entry : entries) { + String key = entry.getSpecies().toLowerCase(); + if (map.containsKey(key)){ + List list = map.get(key); + list.add(entry); + } else { + List list = new ArrayList(); + list.add(entry); + map.put(key, list); + } + } + return map; + } + + /** + * Group a set of wildlife entries by their name + * + * @param filteredEntries + * @return A map of filteredEntries grouped by their name + */ + private static Map> groupSpeciesByName(List filteredEntries) { + Map> filteredMap = new HashMap>(); + + for (Entry entry : filteredEntries) { + String key = entry.getName(); + if (filteredMap.containsKey(key)) { + List list = filteredMap.get(key); + list.add(entry); + } else { + List list = new ArrayList(); + list.add(entry); + filteredMap.put(key, list); + } + } + return filteredMap; + } + + /** + * Prints out the summary + */ + public void execute() throws InvalidInputException { + boolean hasArgument = !this.speciesName.isEmpty(); + + ArrayList entries = EntryList.getArray(); + // Group entries by .species attribute + Map> map = groupEntriesBySpecies(entries); + // change message based on whether `summary` or `summary ` + if (hasArgument) { + SummaryCommandPrinter.printSummaryNameMessage(speciesName); + List filteredEntries = map.get(speciesName); + Map> filteredMap = groupSpeciesByName(filteredEntries); + filteredMap.forEach((key, value) -> { + System.out.println(key + " - (" + value.size() + ")"); + for (Entry v : value) { + System.out.println(v.entryString()); + } + }); + + } else { + SummaryCommandPrinter.printSummarySpecieMessage(); + map.forEach((key, value) -> System.out.println(key + " - (" + value.size() + ")")); + } + } +} diff --git a/src/main/java/seedu/wildwatch/entry/Entry.java b/src/main/java/seedu/wildwatch/entry/Entry.java new file mode 100644 index 0000000000..76c9e6e3b4 --- /dev/null +++ b/src/main/java/seedu/wildwatch/entry/Entry.java @@ -0,0 +1,80 @@ +//@@woodenclock +package seedu.wildwatch.entry; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class Entry { + private LocalDate date; + private String species; + private String name; + private String remark; + + public Entry(String date, String species, String name, String remark) { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + this.date = LocalDate.parse(date, inputFormat); + this.species = species; + this.name = name; + this.remark = remark; + } + + public LocalDate getDate() { + return date; + } + + public String getSpecies() { + return species; + } + + public String getName() { + return name; + } + + public String getRemark() { + return remark; + } + + public void setDate(String date) { + DateTimeFormatter inputFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + this.date = LocalDate.parse(date, inputFormat); + } + + public void setSpecies(String species) { + this.species = species; + } + + public void setName(String name) { + this.name = name; + } + + public void setRemark(String remark) { + this.remark = remark; + } + //@@woodenclock + + public String entryString() { + return String.format("Date: [%s] | Species: [%s] | Name: [%s] | Remark: [%s]", + getDate().format(DateTimeFormatter.ofPattern("dd-MM-yyyy")), getSpecies(), getName(), getRemark()); + } + + /** + * Returns true if both entries have the same value for each field. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Entry)) { + return false; + } + + Entry otherEntry = (Entry) other; + return date.equals(otherEntry.date) + && species.equals(otherEntry.species) + && name.equals(otherEntry.name) + && remark.equals(otherEntry.remark); + } +} diff --git a/src/main/java/seedu/wildwatch/entry/EntryList.java b/src/main/java/seedu/wildwatch/entry/EntryList.java new file mode 100644 index 0000000000..e87ccad77c --- /dev/null +++ b/src/main/java/seedu/wildwatch/entry/EntryList.java @@ -0,0 +1,98 @@ +package seedu.wildwatch.entry; + +import java.time.LocalDate; +import java.util.ArrayList; +import seedu.wildwatch.storage.Saver; + +public class EntryList { + private static ArrayList entries = new ArrayList<>(); //Keeps track of all Entry instances made + + public static void addEntry(Entry newEntry) { + entries.add(newEntry); + } + + public static void clearEntryList(){ + entries.clear(); + } + + public static void deleteEntry(int numberInput){ + entries.remove(numberInput); + } + + public static void editEntry(int nthEntry, Entry updatedEntry) { + entries.set(nthEntry, updatedEntry); + } + + public static ArrayList getArray() { + return entries; + } + + public static int getArraySize() { + return entries.size(); + } + + public static Entry getEntry(int nthEntry) { + return entries.get(nthEntry); + } + + public static LocalDate getEntryDate(int nthEntry) { + return getEntry(nthEntry).getDate(); + } + + public static String getEntrySpecies(int nthEntry) { + return getEntry(nthEntry).getSpecies(); + } + + public static String getEntryName(int nthEntry) { + return getEntry(nthEntry).getName(); + } + + public static String getEntryRemark(int nthEntry) { + return getEntry(nthEntry).getRemark(); + } + + public static boolean isArrayEmpty() { + return entries.isEmpty(); + } + + public static void saveEntry() { + Saver.save(entries); + } + + public static boolean hasSpecies(String speciesName) { + for(int i=0;i < entries.size();i++) { + Entry entry = entries.get(i); + if( entry.getSpecies().equalsIgnoreCase(speciesName) ) { + return true; + } + } + return false; + } + + /** + * Checks if the combination of (date, species, name) from a new entry + * {@code newEntry} is already present in any of the entries in {@code EntryList}. + * Enforce uniqueness of (date, species, name) combination.g + * + * @param newEntry Entry to check + * @return -1 if entry is not in list, 1-index of duplicate otherwise + */ + public static int checkEntryExists(Entry newEntry) { + final LocalDate date = newEntry.getDate(); + final String species = newEntry.getSpecies(); + final String name = newEntry.getName(); + + for (int i = 0; i < entries.size(); i++) { + Entry entry = entries.get(i); + if (entry.getDate().equals(date) + && entry.getSpecies().equals(species) + && entry.getName().equals(name)) { + return i+1; + } + } + + return -1; + } +} + + diff --git a/src/main/java/seedu/wildwatch/error/DateChecker.java b/src/main/java/seedu/wildwatch/error/DateChecker.java new file mode 100644 index 0000000000..007383f773 --- /dev/null +++ b/src/main/java/seedu/wildwatch/error/DateChecker.java @@ -0,0 +1,29 @@ +//@@woodenclock +package seedu.wildwatch.error; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; +import java.util.regex.Pattern; + +public class DateChecker { + private static final Pattern DATE_PATTERN = Pattern.compile( + "(0[1-9]|[1-2][0-9]|3[0-1])-(0[1-9]|1[0-2])-\\d{4}" // dd-mm-yyyy + ); + private static final DateTimeFormatter DATE_FORMATTER = + DateTimeFormatter.ofPattern("dd-MM-uuuu").withResolverStyle(ResolverStyle.STRICT); + + public static boolean isDateValid(String date) { + try { + date = date.trim(); + if (DATE_PATTERN.matcher(date).matches()) { + LocalDate.parse(date, DATE_FORMATTER); + return true; + } + } catch (DateTimeParseException e) { + return false; + } + return false; + } +} diff --git a/src/main/java/seedu/wildwatch/error/ErrorHandler.java b/src/main/java/seedu/wildwatch/error/ErrorHandler.java new file mode 100644 index 0000000000..0dc3172902 --- /dev/null +++ b/src/main/java/seedu/wildwatch/error/ErrorHandler.java @@ -0,0 +1,66 @@ +package seedu.wildwatch.error; + +import java.util.logging.Logger; + +import seedu.wildwatch.command.EditCommand; +import seedu.wildwatch.command.FindCommand; +import seedu.wildwatch.command.HelpCommand; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.ui.ErrorPrinter; +import seedu.wildwatch.ui.ListCommandPrinter; +import seedu.wildwatch.ui.DeleteCommandPrinter; +import seedu.wildwatch.ui.EmptyDescriptionPrinter; + +public class ErrorHandler { + private static final Logger LOGGER = Logger.getLogger(ErrorHandler.class.getName()); + + public static void handleInputError(InvalidInputException exception) { + boolean validInput = false; + + switch (exception.getError()) { + case CUSTOM: + LOGGER.warning("Command is invalid."); + ErrorPrinter.customInvalidInputMessagePrinter(exception.getCustomMessage()); + break; + case EMPTY_INPUT: + LOGGER.warning("Received an empty input."); + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(null); + break; + case EMPTY_EDIT_INPUT: + LOGGER.warning("Received an empty edit input."); + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(EditCommand.COMMAND_WORD); + break; + case EMPTY_FIND_INPUT: + LOGGER.warning("Received an empty find input."); + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(FindCommand.COMMAND_WORD); + break; + case EMPTY_LIST: + LOGGER.info("List is empty."); + ListCommandPrinter.emptyListMessagePrinter(); + validInput = true; + break; + case ENTRY_NOT_FOUND: + LOGGER.warning("Queried entry not found."); + DeleteCommandPrinter.entryNotFoundMessagePrinter(); + validInput = true; + break; + case INVALID_DATE: + LOGGER.warning("Date is invalid."); + ErrorPrinter.invalidDateMessagePrinter(); + break; + case INVALID_INDEX: + LOGGER.warning("Index is invalid."); + ErrorPrinter.invalidIndexMessagePrinter(); + break; + case INVALID_INPUT: + LOGGER.warning("Command is invalid."); + ErrorPrinter.invalidInputMessagePrinter(); + break; + default: + ErrorPrinter.invalidInputMessagePrinter(); + } + if (!validInput) { + HelpCommand.printNeedHelpMessage(); + } + } +} diff --git a/src/main/java/seedu/wildwatch/error/FilenameChecker.java b/src/main/java/seedu/wildwatch/error/FilenameChecker.java new file mode 100644 index 0000000000..58698749ca --- /dev/null +++ b/src/main/java/seedu/wildwatch/error/FilenameChecker.java @@ -0,0 +1,20 @@ +package seedu.wildwatch.error; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FilenameChecker { + + private static final Pattern CSV_FILENAME_FORMAT = Pattern.compile("\\w+.csv"); + + /** + * Checks if filename for a csv file is in the format {@code [a-zA-Z0-9_].csv}. + * + * @param filename Input to test + * @return true if filename is valid, else false + */ + public static boolean isValidCsvFilenameChecker(String filename) { + final Matcher matcher = CSV_FILENAME_FORMAT.matcher(filename); + return matcher.matches(); + } +} diff --git a/src/main/java/seedu/wildwatch/error/InvalidInputErrorType.java b/src/main/java/seedu/wildwatch/error/InvalidInputErrorType.java new file mode 100644 index 0000000000..70090786a1 --- /dev/null +++ b/src/main/java/seedu/wildwatch/error/InvalidInputErrorType.java @@ -0,0 +1,13 @@ +package seedu.wildwatch.error; + +public enum InvalidInputErrorType { + CUSTOM, + EMPTY_INPUT, + EMPTY_EDIT_INPUT, + EMPTY_FIND_INPUT, + EMPTY_LIST, + ENTRY_NOT_FOUND, + INVALID_DATE, + INVALID_INDEX, + INVALID_INPUT +} diff --git a/src/main/java/seedu/wildwatch/exception/InvalidInputException.java b/src/main/java/seedu/wildwatch/exception/InvalidInputException.java new file mode 100644 index 0000000000..f208050e56 --- /dev/null +++ b/src/main/java/seedu/wildwatch/exception/InvalidInputException.java @@ -0,0 +1,29 @@ +package seedu.wildwatch.exception; + +import seedu.wildwatch.error.InvalidInputErrorType; + +public class InvalidInputException extends Exception { + private InvalidInputErrorType errorType; + private String customMessage; + + public InvalidInputException(InvalidInputErrorType error, String message) { + errorType = error; + customMessage = message; + } + + public InvalidInputException(InvalidInputErrorType error) { + this(error, "Command is invalid."); + } + + public InvalidInputException(String message) { + this(InvalidInputErrorType.CUSTOM, message); + } + + public InvalidInputErrorType getError() { + return errorType; + } + + public String getCustomMessage() { + return customMessage; + } +} diff --git a/src/main/java/seedu/wildwatch/exception/UnknownDateFormatException.java b/src/main/java/seedu/wildwatch/exception/UnknownDateFormatException.java new file mode 100644 index 0000000000..0861709772 --- /dev/null +++ b/src/main/java/seedu/wildwatch/exception/UnknownDateFormatException.java @@ -0,0 +1,4 @@ +package seedu.wildwatch.exception; + +public class UnknownDateFormatException extends Exception { +} diff --git a/src/main/java/seedu/wildwatch/execute/CommandHandler.java b/src/main/java/seedu/wildwatch/execute/CommandHandler.java new file mode 100644 index 0000000000..b7ed0d33c9 --- /dev/null +++ b/src/main/java/seedu/wildwatch/execute/CommandHandler.java @@ -0,0 +1,25 @@ +package seedu.wildwatch.execute; + +import seedu.wildwatch.command.Command; +import seedu.wildwatch.command.ByeCommand; +import seedu.wildwatch.error.ErrorHandler; +import seedu.wildwatch.exception.InvalidInputException; + +public class CommandHandler { + public static boolean processCommand(String input) { + try { + Command command = Router.route(input); //Checks if the input command is valid + if (command instanceof ByeCommand) { + return false; + } + executeCommand(command); + } catch (InvalidInputException exception) { + ErrorHandler.handleInputError(exception); + } + return true; + } + + public static void executeCommand(Command command) throws InvalidInputException { + command.execute(); + } +} diff --git a/src/main/java/seedu/wildwatch/execute/InputHandler.java b/src/main/java/seedu/wildwatch/execute/InputHandler.java new file mode 100644 index 0000000000..021af9aedd --- /dev/null +++ b/src/main/java/seedu/wildwatch/execute/InputHandler.java @@ -0,0 +1,34 @@ +//@@woodenclock +package seedu.wildwatch.execute; + +import java.util.logging.Level; +import java.util.logging.Logger; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.command.ByeCommand; +import seedu.wildwatch.ui.InputConsole; +import seedu.wildwatch.ui.LinePrinter; +import seedu.wildwatch.ui.InputPromptPrinter; + +public class InputHandler { + private static final Logger LOGGER = Logger.getLogger(InputHandler.class.getName()); + + /** + * Runs the WildWatch by looping through the I/O process. + * If "bye" is received, + */ + public static void handleInput() { + boolean loopFlag = true; + while (loopFlag) { + InputPromptPrinter.inputPromptPrinter(); + String input = InputConsole.inputRetriever(); //Retrieves input of user + if (!input.equals("bye")) { + LinePrinter.printHorizontalLines(); + } + LOGGER.log(Level.INFO, "Input received: {0}", input); + loopFlag = CommandHandler.processCommand(input); + EntryList.saveEntry(); + LinePrinter.printHorizontalLines(); + } + ByeCommand.exitProgram(); + } +} diff --git a/src/main/java/seedu/wildwatch/execute/Router.java b/src/main/java/seedu/wildwatch/execute/Router.java new file mode 100644 index 0000000000..f3f1bf0868 --- /dev/null +++ b/src/main/java/seedu/wildwatch/execute/Router.java @@ -0,0 +1,79 @@ +//@@woodenclock +package seedu.wildwatch.execute; + +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.wildwatch.command.Command; +import seedu.wildwatch.command.EditCommand; +import seedu.wildwatch.command.SummaryCommand; +import seedu.wildwatch.command.ListCommand; +import seedu.wildwatch.command.AddCommand; +import seedu.wildwatch.command.ByeCommand; +import seedu.wildwatch.command.DeleteCommand; +import seedu.wildwatch.command.ExportCommand; +import seedu.wildwatch.command.FindCommand; +import seedu.wildwatch.command.HelpCommand; + +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.parser.AddCommandParser; +import seedu.wildwatch.parser.DeleteCommandParser; +import seedu.wildwatch.parser.EditCommandParser; +import seedu.wildwatch.parser.FindCommandParser; +import seedu.wildwatch.parser.ListCommandParser; +import seedu.wildwatch.parser.SummaryCommandParser; +import seedu.wildwatch.parser.ExportCommandParser; + + +/** + * Identifies the input command and sends it to the corresponding parser. + */ +public class Router { + private static final Logger LOGGER = Logger.getLogger(Router.class.getName()); + + public static Command route(String input) throws InvalidInputException { + + LOGGER.log(Level.INFO, "Routing entry for input: {0}", input); + Scanner bufferScanner = new Scanner(input); //Scanner for the buffer + if (!bufferScanner.hasNext()) { + throw new InvalidInputException(InvalidInputErrorType.EMPTY_INPUT); + } + String firstWord = bufferScanner.next(); //Stores first word in the input + + //Functionalities + switch (firstWord) { + case AddCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Add command was input: {0}", input); + return new AddCommandParser().parse(input); + case DeleteCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Delete command was input: {0}", input); + return new DeleteCommandParser().parse(input); + case EditCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Edit command was input: {0}", input); + return new EditCommandParser().parse(input); + case FindCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Find command was input: {0}", input); + return new FindCommandParser().parse(input); + case ListCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "List command was input: {0}", input); + return new ListCommandParser().parse(input); + case SummaryCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Summary command was input: {0}", input); + return new SummaryCommandParser().parse(input); + case ExportCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Export command was input: {0}", input); + return new ExportCommandParser().parse(input); + case HelpCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Help command was input: {0}", input); + return new HelpCommand(input); + case ByeCommand.COMMAND_WORD: + LOGGER.log(Level.INFO, "Exit command was input: {0}", input); + return new ByeCommand(); + default: + LOGGER.log(Level.WARNING, "Invalid input received: {0}. Throwing exception.", input); + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + } +} diff --git a/src/main/java/seedu/wildwatch/miscellaneous/BootUp.java b/src/main/java/seedu/wildwatch/miscellaneous/BootUp.java new file mode 100644 index 0000000000..030c284f8f --- /dev/null +++ b/src/main/java/seedu/wildwatch/miscellaneous/BootUp.java @@ -0,0 +1,38 @@ +//@@woodenclock +package seedu.wildwatch.miscellaneous; + +import seedu.wildwatch.ui.LinePrinter; + +public class BootUp { + public static final String WELCOME_MESSAGE_1 = + "Hello there! Welcome to WildWatch!\n\n"; + + public static final String WELCOME_MESSAGE_2 = + "What would you like to do?\n"; + + public static final String LOGO = + "____ __ ____ __ __ _______\n" + + "\\ \\ / \\ / / | | | | | \\\n" + + " \\ \\/ \\/ / | | | | | .--. |\n" + + " \\ / | | | | | | | |\n" + + " \\ /\\ / | | | `----.| '--' |\n" + + " \\__/ \\__/ |__| |_______||_______/\n" + + "____ __ ____ ___ .___________. ______ __ __\n" + + "\\ \\ / \\ / / / \\ | | / || | | |\n" + + " \\ \\/ \\/ / / ^ \\ `---| |----`| ,----'| |__| |\n" + + " \\ / / /_\\ \\ | | | | | __ |\n" + + " \\ /\\ / / _____ \\ | | | `----.| | | |\n" + + " \\__/ \\__/ /__/ \\__\\ |__| \\______||__| |__|\n"; + + public static void bootUpOne() { + LinePrinter.printHorizontalLines(); + System.out.print(BootUp.LOGO); + LinePrinter.printHorizontalLines(); + System.out.print(BootUp.WELCOME_MESSAGE_1); + } + + public static void bootUpTwo() { + System.out.print(BootUp.WELCOME_MESSAGE_2); + LinePrinter.printHorizontalLines(); + } +} diff --git a/src/main/java/seedu/wildwatch/miscellaneous/LogHandler.java b/src/main/java/seedu/wildwatch/miscellaneous/LogHandler.java new file mode 100644 index 0000000000..83bded04b8 --- /dev/null +++ b/src/main/java/seedu/wildwatch/miscellaneous/LogHandler.java @@ -0,0 +1,40 @@ +package seedu.wildwatch.miscellaneous; + +import seedu.wildwatch.ui.ErrorPrinter; + +import java.util.logging.Logger; +import java.util.logging.Handler; +import java.util.logging.FileHandler; +import java.util.logging.SimpleFormatter; + +public class LogHandler { + private static final Logger LOGGER = Logger.getLogger(LogHandler.class.getName()); + + static { + try { + // Configure a FileHandler + FileHandler file = new FileHandler("WildWatch.log", true); + SimpleFormatter formatter = new SimpleFormatter(); + file.setFormatter(formatter); + + // Obtain the root logger. An empty string gets the root logger + Logger rootLogger = Logger.getLogger(""); + + // Remove the default handlers (like ConsoleHandler) + for (Handler handler : rootLogger.getHandlers()) { + rootLogger.removeHandler(handler); + } + + // Add the FileHandler to the root logger + rootLogger.addHandler(file); + } catch (Exception exception) { + LOGGER.warning("Queried entry not found."); + ErrorPrinter.corruptLoggerMessagePrinter(); + ShutDown.shutDownWithError(); + System.exit(0); + } + } + + public static void configure() { // Call this empty method to ensure the static block runs + } +} diff --git a/src/main/java/seedu/wildwatch/miscellaneous/ShutDown.java b/src/main/java/seedu/wildwatch/miscellaneous/ShutDown.java new file mode 100644 index 0000000000..a161c22756 --- /dev/null +++ b/src/main/java/seedu/wildwatch/miscellaneous/ShutDown.java @@ -0,0 +1,18 @@ +//@@woodenclock +package seedu.wildwatch.miscellaneous; + +import seedu.wildwatch.ui.LinePrinter; + +public class ShutDown { + public static void shutDown() { + //Program Ending + System.out.println("Bye. Hope to see you again soon!"); + LinePrinter.printHorizontalLines(); + } + + public static void shutDownWithError() { + //Program Ending + System.out.println("Ending program due to error..."); + LinePrinter.printHorizontalLines(); + } +} diff --git a/src/main/java/seedu/wildwatch/parser/AddCommandParser.java b/src/main/java/seedu/wildwatch/parser/AddCommandParser.java new file mode 100644 index 0000000000..934f01ced1 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/AddCommandParser.java @@ -0,0 +1,222 @@ +//@@lctxct +package seedu.wildwatch.parser; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.command.AddCommand; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.error.DateChecker; +import seedu.wildwatch.error.InvalidInputErrorType; + +import java.util.Scanner; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +public class AddCommandParser implements Parser { + + + private static final String[] ENTRY_ITEMS = new String[] { "date", "species", "name", "remark" }; + + private static final Pattern ADD_DEFAULT_COMMAND_FORMAT_CHECK = + Pattern.compile("add" + + "\\s*(? D/)?\\s*(?[^/]+)?" + + "\\s*(? S/)?\\s*(?[^/]+)?" + + "\\s*(? N/)?\\s*(?[^/]+)?" + + "\\s*(?: R/(?.*))?"); + + private String date; + private String species; + private String name; + private String remark = null; + + @Override + public AddCommand parse(String input) throws InvalidInputException { + + final Matcher matcherInteractive = AddCommand.ADD_INTERACTIVE_COMMAND_FORMAT.matcher(input); + if (matcherInteractive.matches()) { + handleInteractive(); + } else { + // Default case + performChecks(input); + + final Matcher matcherDefault = AddCommand.ADD_DEFAULT_COMMAND_FORMAT.matcher(input); + boolean isMatch = matcherDefault.matches(); + + assert isMatch : "Command should match format."; + + date = matcherDefault.group("date").trim(); + species = matcherDefault.group("species").trim(); + name = matcherDefault.group("name").trim(); + remark = matcherDefault.group("remark"); + + if (remark == null) { + remark = ""; + } else { + remark = remark.trim(); + } + } + + Entry newEntry = new Entry(date, species, name, remark); + + // Check that entry is not a duplicate + int duplicateEntryIdx = EntryList.checkEntryExists(newEntry); + if (duplicateEntryIdx != -1) { + throw new InvalidInputException("Entry already exists! " + + "Note that you can only add one entry per animal in a day.\n" + + "Use the edit command or delete the existing entry first."); + } + + return new AddCommand(newEntry); + } + + /** + * Handle interactive add command, which prompts user for each item required + * in an entry. + */ + private void handleInteractive() throws InvalidInputException { + Scanner scanner = new Scanner(System.in); + String input; + + for (String item : ENTRY_ITEMS) { + String promptMessage = String.format("Please input the %s you would like to set, or q/ to quit.", item); + if (item.equals("remark")) { + promptMessage += "\n(Hit Enter to leave this field blank)"; + } + + input = getItem(scanner, promptMessage, item); + + switch (item) { + case "date": + date = input; + break; + case "species": + species = input; + break; + case "name": + name = input; + break; + case "remark": + remark = input; + break; + default: + } + } + } + + private String getItem(Scanner scanner, String promptMessage, String item) throws InvalidInputException { + String inputBuffer; + boolean isValid = false; + + do { + System.out.print(promptMessage); + System.out.println(": "); + + + inputBuffer = scanner.nextLine().trim(); + isValid = true; + + // check if user q/uits early + if (inputBuffer.equals("q/")) { + throw new InvalidInputException("Exited add command"); + } + + // check that fields are not empty + if (!item.equals("remark")) { + if (inputBuffer.isEmpty()) { + isValid = false; + System.out.println("Field cannot be left blank."); + } + } + + // if date, check validity of date + if (item.equals("date")) { + if (!DateChecker.isDateValid(inputBuffer)) { + isValid = false; + System.out.println("Please key in a valid date."); + } + } + + // check that no / + if (inputBuffer.contains("/")) { + isValid = false; + System.out.println("Your input should not contain /."); + } + + } while (!isValid); + + return inputBuffer; + } + + /** + * Ensures that {@code input} adheres to the command format. + * + * @param input + * @throws InvalidInputException thrown if the input does not adhere to command format. + */ + private void performChecks(String input) throws InvalidInputException { + final Matcher matcher = ADD_DEFAULT_COMMAND_FORMAT_CHECK.matcher(input); + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + checkDate(matcher); + checkSpecies(matcher); + checkName(matcher); + } + + /** + * Checks that date is correctly defined in input. Checks the following: + * 1. D/ is present in user input + * 2. A date value is defined + * 3. Date provided is correctly formatted + * + * @param matcher Input matched against command pattern + * @throws InvalidInputException thrown when date is not correctly defined + */ + private void checkDate(Matcher matcher) throws InvalidInputException { + if (matcher.group("dprefix") == null) { + throw new InvalidInputException("D/ is not defined"); + } + if (matcher.group("date") == null) { + throw new InvalidInputException("Date value cannot be empty."); + } + String date = matcher.group("date").trim(); + if (!DateChecker.isDateValid(date)) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_DATE); + } + } + + /** + * Checks that species is correctly defined in input. Checks the following: + * 1. S/ is present in user input + * 2. Species is defined + * + * @param matcher Input matched against command pattern + * @throws InvalidInputException thrown when species is not correctly defined + */ + private void checkSpecies(Matcher matcher) throws InvalidInputException { + if (matcher.group("sprefix") == null) { + throw new InvalidInputException("S/ is not defined"); + } + if (matcher.group("species") == null) { + throw new InvalidInputException("Species is not defined."); + } + } + + /** + * Checks that name is correctly defined in input. Checks the following: + * 1. N/ is present in user input + * 2. Name is defined + * + * @param matcher Input matched against command pattern + * @throws InvalidInputException thrown when species is not correctly defined + */ + private void checkName(Matcher matcher) throws InvalidInputException { + if (matcher.group("nprefix") == null) { + throw new InvalidInputException("N/ is not defined"); + } + if (matcher.group("name") == null) { + throw new InvalidInputException("Name is not defined"); + } + } +} diff --git a/src/main/java/seedu/wildwatch/parser/DeleteCommandParser.java b/src/main/java/seedu/wildwatch/parser/DeleteCommandParser.java new file mode 100644 index 0000000000..cbcaae26c0 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/DeleteCommandParser.java @@ -0,0 +1,56 @@ +//@@lctxct +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.DeleteCommand; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.error.InvalidInputErrorType; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +public class DeleteCommandParser implements Parser { + + private static final Pattern DELETE_ENTRY_COMMAND_FORMAT_CHECK = + Pattern.compile("delete\\s+(?\\S+)\\s*"); + + @Override + public DeleteCommand parse(String input) throws InvalidInputException { + int getIdx = getIndexFromInput(input); + + return new DeleteCommand(getIdx); + } + + /** + * Gets index of entry to delete from input. + * Checks that input adheres to required delete command format. + * 1. Checks that it is in the form {@code deleteINDEX} + * 2. Checks that INDEX > 1 and INDEX <= length of entry list + * + * @param input + * @return index of entry to delete + * @throws InvalidInputException thrown when command format is incorrect. + */ + private int getIndexFromInput(String input) throws InvalidInputException { + + final Matcher matcher = DELETE_ENTRY_COMMAND_FORMAT_CHECK.matcher(input); + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + // Check that index is a valid number + int deleteIdx = -1; + try { + deleteIdx = Integer.parseInt(matcher.group("index")); + } catch (NumberFormatException e) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INDEX); + } + + // Check that index is within bounds + if (deleteIdx < 1 || deleteIdx > EntryList.getArraySize()) { + throw new InvalidInputException(InvalidInputErrorType.ENTRY_NOT_FOUND); + } + + return deleteIdx; + } +} diff --git a/src/main/java/seedu/wildwatch/parser/EditCommandParser.java b/src/main/java/seedu/wildwatch/parser/EditCommandParser.java new file mode 100644 index 0000000000..485e9c4c22 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/EditCommandParser.java @@ -0,0 +1,76 @@ +//@@woodenclock +package seedu.wildwatch.parser; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import seedu.wildwatch.command.Command; +import seedu.wildwatch.command.EditCommand; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.error.DateChecker; +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; + +public class EditCommandParser { + private static final Pattern EDIT_ENTRY_COMMAND_FORMAT_CHECK = + Pattern.compile("edit" + + "\\s*(? I/)?\\s*(?[^/]+)" + + "\\s*(? D/)?\\s*(?[^/]+)?" + + "\\s*(? S/)?\\s*(?[^/]+)?" + + "\\s*(? N/)?\\s*(?[^/]+)?" + + "\\s*(?: R/(?[^/]*))?"); + + public Command parse(String input) throws InvalidInputException { + performChecks(input); + + final Matcher matcher = EditCommand.EDIT_ENTRY_COMMAND_FORMAT.matcher(input); + boolean isMatch = matcher.matches(); + assert isMatch : "Command should match format."; + + return new EditCommand(input); + } + + /** + * Ensures that {@code input} adheres to the command format. + * + * @param input + * @throws InvalidInputException thrown if the input does not adhere to command format. + */ + private void performChecks(String input) throws InvalidInputException { + final Matcher matcher = EDIT_ENTRY_COMMAND_FORMAT_CHECK.matcher(input); + + //Checks if the input adheres to the command format + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + checkIndex(matcher); + if (!(matcher.group("date") == null)) { + checkDate(matcher); + } + + //Checks if there is any change to edit + if ((matcher.group("date") == null) && (matcher.group("species") == null) && + (matcher.group("name") == null) && (matcher.group("remark") == null)) { + throw new InvalidInputException(InvalidInputErrorType.EMPTY_EDIT_INPUT); + } + } + + private void checkIndex(Matcher matcher) throws InvalidInputException{ + int editIdx = -1; + try { + editIdx = Integer.parseInt(matcher.group("index")); + } catch (NumberFormatException exception) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INDEX); + } + if ((matcher.group("index") == null) || editIdx < 1 || editIdx > EntryList.getArraySize()) { + throw new InvalidInputException(InvalidInputErrorType.ENTRY_NOT_FOUND); + } + } + + private void checkDate(Matcher matcher) throws InvalidInputException { + String dateValue = matcher.group("date"); + if (!DateChecker.isDateValid(dateValue)) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_DATE); + } + } +} diff --git a/src/main/java/seedu/wildwatch/parser/ExportCommandParser.java b/src/main/java/seedu/wildwatch/parser/ExportCommandParser.java new file mode 100644 index 0000000000..5fec7472cd --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/ExportCommandParser.java @@ -0,0 +1,51 @@ +//@@lctxct +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.ExportCommand; +import seedu.wildwatch.error.FilenameChecker; +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; + +import java.util.Scanner; +import java.util.regex.Matcher; + +/** + * Parses export command. Accepts optional {@code filename} argument + * proceeding {@code export} keyword. + */ +public class ExportCommandParser implements Parser { + + @Override + public ExportCommand parse(String input) throws InvalidInputException { + final Matcher matcher = ExportCommand.EXPORT_COMMAND_FORMAT.matcher(input); + + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + String filename = matcher.group("filename"); + if (filename == null) { + return new ExportCommand(); + } + + Scanner scanner = new Scanner(System.in); + + // Keep prompting until user provides a valid filename or decides to exit + while (!FilenameChecker.isValidCsvFilenameChecker(filename)) { + System.out.println("Filename is invalid!"); + System.out.println("Please provide a valid filename, or leave input blank to default to WildWatch.csv."); + System.out.print("You may also type q/ to exit command: "); + filename = scanner.nextLine().trim(); + + if (filename.equals("q/")) { + throw new InvalidInputException("Exiting export command..."); + } + + if (filename.isEmpty()) { + return new ExportCommand(); + } + } + + return new ExportCommand(filename); + } +} diff --git a/src/main/java/seedu/wildwatch/parser/FileStringParser.java b/src/main/java/seedu/wildwatch/parser/FileStringParser.java new file mode 100644 index 0000000000..df37c08f66 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/FileStringParser.java @@ -0,0 +1,66 @@ +//@@lctxct +package seedu.wildwatch.parser; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.command.AddFileStringCommand; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.error.DateChecker; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +public class FileStringParser implements Parser { + + private static final Pattern ADD_FILE_ENTRY_COMMAND_FORMAT_CHECK = + Pattern.compile( + "\\s*(?[^/]+)?\\s*/" + + "\\s*(?[^/]+)?\\s*/" + + "\\s*(?[^/]+)?\\s*/" + + "\\s*(?[^/]+)?"); + + @Override + public AddFileStringCommand parse(String input) throws InvalidInputException { + performChecks(input); + + final Matcher matcher = AddFileStringCommand.FILE_STRING_FORMAT.matcher(input); + boolean isMatch = matcher.matches(); + + assert isMatch : "Input line should match format."; + + final String date = matcher.group("date").trim(); + final String species = matcher.group("species").trim(); + final String name = matcher.group("name").trim(); + final String remark = matcher.group("remark").trim(); + + Entry newEntry = new Entry(date, species, name, remark); + + int existingEntryIdx = EntryList.checkEntryExists(newEntry); + if (existingEntryIdx != -1) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + return new AddFileStringCommand(newEntry); + } + + private void performChecks(String input) throws InvalidInputException { + + final Matcher matcher = ADD_FILE_ENTRY_COMMAND_FORMAT_CHECK.matcher(input); + if (!matcher.matches()) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + // Check that all groups present + if (matcher.group("date") == null || + matcher.group("species") == null || + matcher.group("name") == null) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + + // Check that date is valid + if (!DateChecker.isDateValid(matcher.group("date"))) { + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } + } +} diff --git a/src/main/java/seedu/wildwatch/parser/FindCommandParser.java b/src/main/java/seedu/wildwatch/parser/FindCommandParser.java new file mode 100644 index 0000000000..bba1b32fd7 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/FindCommandParser.java @@ -0,0 +1,20 @@ +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.FindCommand; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.error.InvalidInputErrorType; + +public class FindCommandParser implements Parser{ + public static final String COMMAND_WORD = "find"; + private static final int NUMBER_OF_ALPHABETS_IN_FIND = 4; + + @Override + public FindCommand parse(String input) throws InvalidInputException { + String matchingWord = input.substring(input.indexOf(COMMAND_WORD) + + NUMBER_OF_ALPHABETS_IN_FIND).trim(); + if (matchingWord.isEmpty()) { + throw new InvalidInputException(InvalidInputErrorType.EMPTY_FIND_INPUT); + } + return new FindCommand(matchingWord); //Note the "find" is removed from the String here + } +} diff --git a/src/main/java/seedu/wildwatch/parser/ListCommandParser.java b/src/main/java/seedu/wildwatch/parser/ListCommandParser.java new file mode 100644 index 0000000000..aab8a3b154 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/ListCommandParser.java @@ -0,0 +1,20 @@ +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.ListCommand; +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; + +public class ListCommandParser implements Parser{ + public static final String COMMAND_WORD = "list"; + private static final int NUMBER_OF_ALPHABETS_IN_LIST = 4; + + + public ListCommand parse(String input) throws InvalidInputException { + String matchingWord = input.substring(input.indexOf(COMMAND_WORD) + + NUMBER_OF_ALPHABETS_IN_LIST).trim(); + if (matchingWord.isEmpty()) { + return new ListCommand(); + } + throw new InvalidInputException(InvalidInputErrorType.INVALID_INPUT); + } +} diff --git a/src/main/java/seedu/wildwatch/parser/Parser.java b/src/main/java/seedu/wildwatch/parser/Parser.java new file mode 100644 index 0000000000..8e03e639a8 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/Parser.java @@ -0,0 +1,13 @@ +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.Command; +import seedu.wildwatch.exception.InvalidInputException; + +/** + * Returns a specific command object as defined in the command package. + * + * @param + */ +public interface Parser { + T parse(String input) throws InvalidInputException; +} diff --git a/src/main/java/seedu/wildwatch/parser/SummaryCommandParser.java b/src/main/java/seedu/wildwatch/parser/SummaryCommandParser.java new file mode 100644 index 0000000000..90635b6780 --- /dev/null +++ b/src/main/java/seedu/wildwatch/parser/SummaryCommandParser.java @@ -0,0 +1,34 @@ +package seedu.wildwatch.parser; + +import seedu.wildwatch.command.SummaryCommand; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; + +public class SummaryCommandParser implements Parser { + + private static final String COMMAND_WORD = "summary"; + + @Override + public SummaryCommand parse(String input) throws InvalidInputException { + String speciesName = getArgumentFromInput(input).toLowerCase(); + // speciesName can be blank - i.e. no argument specified + if( !speciesName.isEmpty() ) { + // if there is an argument specified, check if it + boolean hasSpecies = EntryList.hasSpecies(speciesName); + if( !hasSpecies ) { + throw new InvalidInputException("Species does not exists !"); + } + } + return new SummaryCommand(speciesName); + } + + /** + * Gets the argument (species name) if specified. + * + * @param input + * @return species name if specified, otherwise an empty string. + */ + protected String getArgumentFromInput(String input) { + return input.replace("summary", "").trim(); + } +} diff --git a/src/main/java/seedu/wildwatch/storage/EntryToStringConverter.java b/src/main/java/seedu/wildwatch/storage/EntryToStringConverter.java new file mode 100644 index 0000000000..cf7c4f4cb3 --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/EntryToStringConverter.java @@ -0,0 +1,60 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import seedu.wildwatch.entry.Entry; + +import java.io.IOException; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; + +public class EntryToStringConverter { + private static final DateTimeFormatter STD_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + + private static final DateTimeFormatter CSV_DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + + /** + * Returns String that is in the format to be written to file. + * + * @param entry Entry of interest. + * @return String. + * @throws IOException when there is problem with formatting the task. + */ + public static String toFileString(Entry entry) throws IOException { + assert entry != null : "Trying to convert a null entry to file string."; + String date = entry.getDate().format(STD_FORMAT); + String species = entry.getSpecies(); + String name = entry.getName(); + String remark = entry.getRemark(); + + return String.format("%s / %s / %s / %s", date, species, name, remark); + } + //@@woodenclock + + public static String toCsvString(Entry entry, int id, ArrayList columns) { + assert entry != null : "Trying to convert a null entry to file string."; + + String items = String.format("%d", id); + + if (columns.contains("date")) { + String date = entry.getDate().format(CSV_DATE_FORMAT); + items = String.join(",", items, date); + } + + if (columns.contains("species")) { + String species = entry.getSpecies(); + items = String.join(",", items, species); + } + + if (columns.contains("name")) { + String name = entry.getName(); + items = String.join(",", items, name); + } + + if (columns.contains("remark")) { + String remark = entry.getRemark(); + items = String.join(",", items, remark); + } + + return items + "\n"; + } +} diff --git a/src/main/java/seedu/wildwatch/storage/ExistenceChecker.java b/src/main/java/seedu/wildwatch/storage/ExistenceChecker.java new file mode 100644 index 0000000000..0f65b15c3a --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/ExistenceChecker.java @@ -0,0 +1,25 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import java.io.File; + +import seedu.wildwatch.ui.FilePrinter; + +public class ExistenceChecker { + /** + * Checks if file exists. Opens file if the file exists. + * + * @return true if file exists; false if not. + */ + public static boolean checkFileExistence(String filename) { + assert filename != null : "File path should not be null."; + + FilePrinter.checkingIfFileExistsMessagePrinter(filename); + File file = new File(filename); + if (!file.exists()) { + return false; + } else { + return true; + } + } +} diff --git a/src/main/java/seedu/wildwatch/storage/FileCreator.java b/src/main/java/seedu/wildwatch/storage/FileCreator.java new file mode 100644 index 0000000000..3be247621f --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/FileCreator.java @@ -0,0 +1,22 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import java.io.File; +import java.io.IOException; + +import seedu.wildwatch.ui.FilePrinter; + +public class FileCreator { + /** + * Creates new file with filename specified by {@code FILE_PATH}. + */ + public static File createFile(String filename) throws IOException { + File file = new File(filename); + if (file.createNewFile()) { + FilePrinter.createNewFileMessagePrinter(filename); + return file; + } else { + throw new IOException(); + } + } +} diff --git a/src/main/java/seedu/wildwatch/storage/FileHandler.java b/src/main/java/seedu/wildwatch/storage/FileHandler.java new file mode 100644 index 0000000000..e734f318a7 --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/FileHandler.java @@ -0,0 +1,51 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import seedu.wildwatch.command.ListCommand; +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.miscellaneous.BootUp; +import seedu.wildwatch.miscellaneous.ShutDown; +import seedu.wildwatch.ui.FilePrinter; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class FileHandler { + private static final String FILE_NAME = "WildWatch.txt"; + private static File openedFile; //File to save the entries + + public static void handleFile() { + if (ExistenceChecker.checkFileExistence(FILE_NAME)) { + FilePrinter.fileExistMessagePrinter(); + FilePrinter.openingFileMessagePrinter(); + openedFile = new File(FILE_NAME); + try { + FileLoader.loadFile(openedFile); // Loads entries from file + assert openedFile != null : "Trying to read from a non-initialized file."; + FilePrinter.entriesLoadedMessagePrinter(); + + // Lists entries loaded from file + ListCommand.listEntry(); + } catch (FileNotFoundException exception) { + FilePrinter.noFileMessagePrinter(); + } catch (InvalidInputException e) { + FilePrinter.corruptFileMessagePrinter(); + ShutDown.shutDownWithError(); + System.exit(0); + } + } else { // File does not exist + FilePrinter.noFileMessagePrinter(); + try { + openedFile = FileCreator.createFile(FILE_NAME); + } catch (IOException exception) { + FilePrinter.fileCreationFailMessagePrinter(); + ShutDown.shutDownWithError(); + System.exit(0); + } + assert openedFile.exists() : "File was supposed to be created but it doesn't exist."; + } + + BootUp.bootUpTwo(); //Welcome prompt message + } +} diff --git a/src/main/java/seedu/wildwatch/storage/FileLoader.java b/src/main/java/seedu/wildwatch/storage/FileLoader.java new file mode 100644 index 0000000000..6ea5032b53 --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/FileLoader.java @@ -0,0 +1,24 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import java.util.Scanner; +import java.io.File; +import java.io.FileNotFoundException; + +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.parser.FileStringParser; + + +public class FileLoader { + /** + * Loads tasks from file into the TaskList. + */ + public static void loadFile(File openedFile) throws FileNotFoundException, InvalidInputException { + Scanner fileReader = new Scanner(openedFile); // create a Scanner using the File as the source + while (fileReader.hasNext()) { + String lineOfFile = fileReader.nextLine(); + + new FileStringParser().parse(lineOfFile).execute(); // Creates new entry from line in file + } + } +} diff --git a/src/main/java/seedu/wildwatch/storage/Saver.java b/src/main/java/seedu/wildwatch/storage/Saver.java new file mode 100644 index 0000000000..29c299168b --- /dev/null +++ b/src/main/java/seedu/wildwatch/storage/Saver.java @@ -0,0 +1,30 @@ +//@@woodenclock +package seedu.wildwatch.storage; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.ui.ErrorPrinter; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +public class Saver { + private static final String FILE_PATH = "./WildWatch.txt"; + + /** + * Manages the storage of tasks in a file. + * Write each task in the desired format to the file. + */ + public static void save(ArrayList entries) { + assert entries != null : "Trying to save a null list of entries."; + try { + FileWriter writer = new FileWriter(FILE_PATH); + for (Entry entry : entries) { + writer.write(EntryToStringConverter.toFileString(entry) + "\n"); + } + writer.close(); + } catch (IOException exception) { + ErrorPrinter.errorMessagePrinter(exception); + } + } +} diff --git a/src/main/java/seedu/wildwatch/ui/AddCommandPrinter.java b/src/main/java/seedu/wildwatch/ui/AddCommandPrinter.java new file mode 100644 index 0000000000..6acfb24291 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/AddCommandPrinter.java @@ -0,0 +1,7 @@ +package seedu.wildwatch.ui; + +public class AddCommandPrinter { + public static void entryAddedMessagePrinter() { + System.out.println("The following entry has been added:"); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/DeleteCommandPrinter.java b/src/main/java/seedu/wildwatch/ui/DeleteCommandPrinter.java new file mode 100644 index 0000000000..7245034ba5 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/DeleteCommandPrinter.java @@ -0,0 +1,25 @@ +package seedu.wildwatch.ui; + +public class DeleteCommandPrinter { + public static void entryRemovedMessagePrinter() { + LinePrinter.printHorizontalLines(); + System.out.println("The entry has been deleted."); + } + + public static void entryDeletionConfirmationPrinter(int i) { + System.out.println("Are you sure you want to delete this entry? (yes/no): "); + System.out.println("You have " + (5-i) + " number of tries left."); + LinePrinter.printHorizontalLines(); + } + + public static void entryNotDeletedMessagePrinter() { + System.out.println("The entry was not deleted."); + } + + /** + * Prints out appropriate message when nth entry input by the user is not found. + */ + public static void entryNotFoundMessagePrinter() { + System.out.println("OOPS!!! The entry number could not be found :-("); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/EditCommandPrinter.java b/src/main/java/seedu/wildwatch/ui/EditCommandPrinter.java new file mode 100644 index 0000000000..b607e89b75 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/EditCommandPrinter.java @@ -0,0 +1,7 @@ +package seedu.wildwatch.ui; + +public class EditCommandPrinter { + public static void entryEditedMessagePrinter() { + System.out.println("The following entry has been edited:"); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/EmptyDescriptionPrinter.java b/src/main/java/seedu/wildwatch/ui/EmptyDescriptionPrinter.java new file mode 100644 index 0000000000..fef7adafa0 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/EmptyDescriptionPrinter.java @@ -0,0 +1,24 @@ +package seedu.wildwatch.ui; + +import seedu.wildwatch.command.AddCommand; +import seedu.wildwatch.command.DeleteCommand; +import seedu.wildwatch.command.EditCommand; +import seedu.wildwatch.command.FindCommand; + +public class EmptyDescriptionPrinter { + public static void emptyDescriptionMessagePrinter(String description) { + if (description == null) { + System.out.println("OOPS!!! The description cannot be empty. :-("); + } else if (description.equals(AddCommand.COMMAND_WORD)) { + System.out.println("OOPS!!! The description of an add command cannot be empty. :-("); + } else if (description.equals(DeleteCommand.COMMAND_WORD)) { + System.out.println("OOPS!!! The description of a delete command cannot be empty. :-("); + } else if (description.equals(EditCommand.COMMAND_WORD)) { + System.out.println("OOPS!!! The description of an edit command cannot be empty. :-("); + } else if (description.equals(FindCommand.COMMAND_WORD)) { + System.out.println("OOPS!!! The description of a find command cannot be empty. :-("); + } else { + System.out.println("OOPS!!! The description cannot be empty. :-("); + } + } +} diff --git a/src/main/java/seedu/wildwatch/ui/EntryPrinter.java b/src/main/java/seedu/wildwatch/ui/EntryPrinter.java new file mode 100644 index 0000000000..134fc7ffb6 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/EntryPrinter.java @@ -0,0 +1,21 @@ +package seedu.wildwatch.ui; + +import seedu.wildwatch.entry.EntryList; + +import java.time.format.DateTimeFormatter; + +public class EntryPrinter { + /** + * Prints the nth entry of the wildlife data stored in WildWatch. + * + * @param nthEntry + */ + public static void printEntry(int nthEntry) { + System.out.print("Date: [" + + EntryList.getEntryDate(nthEntry).format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) + "] | "); + System.out.print("Species: [" + EntryList.getEntrySpecies(nthEntry) + "] | "); + System.out.print("Name: [" + EntryList.getEntryName(nthEntry) + "] | "); + System.out.print("Remark: [" + EntryList.getEntryRemark(nthEntry) + "]"); + System.out.print(System.lineSeparator()); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/ErrorPrinter.java b/src/main/java/seedu/wildwatch/ui/ErrorPrinter.java new file mode 100644 index 0000000000..9f3b932ddb --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/ErrorPrinter.java @@ -0,0 +1,38 @@ +package seedu.wildwatch.ui; + +public class ErrorPrinter { + public static void corruptLoggerMessagePrinter() { + System.out.println("Logger is corrupted.\nUnable to be configured"); + } + + public static void customInvalidInputMessagePrinter(String e) { + System.out.println(e); + } + + /** + * Prints an error message to the console. + * + * @param exception The exception that occurred. + */ + public static void errorMessagePrinter(Exception exception) { + System.out.println("An error occurred: " + exception.getMessage()); + } + + /** + * Prints out appropriate message when invalid input is typed into the program. + */ + public static void invalidInputMessagePrinter() { + System.out.println("OOPS!!! Format of command is incorrect."); + } + + /** + * Prints out appropriate message when the input date is invalid + */ + public static void invalidDateMessagePrinter() { + System.out.println("OOPS!!! Invalid Date input :-("); + } + + public static void invalidIndexMessagePrinter() { + System.out.println("OOPS!!! Invalid Index input :-("); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/FilePrinter.java b/src/main/java/seedu/wildwatch/ui/FilePrinter.java new file mode 100644 index 0000000000..6c850959f3 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/FilePrinter.java @@ -0,0 +1,109 @@ +package seedu.wildwatch.ui; + +public class FilePrinter { + /** + * Prints out appropriate message when file is corrupt + */ + public static void corruptFileMessagePrinter() { + System.out.println("File is corrupted.\nUnable to read file"); + } + + /** + * Prints out message after a new file has been created. + */ + public static void createNewFileMessagePrinter(String filename) { + System.out.println("Creating new file...\nNew empty file: \"" + + filename + "\" created successfully."); + } + + /** + * Prints out appropriate message when creation of new file failed + */ + public static void fileCreationFailMessagePrinter() { + System.out.println("File creation failed."); + } + + /** + * Prints out appropriate message when file already exists. + */ + public static void fileExistMessagePrinter() { + System.out.println("File already exists."); + } + + /** + * Prints out appropriate message when opening a file. + */ + public static void openingFileMessagePrinter() { + System.out.println("Opening existing file...\n"); + } + + /** + * Prints out appropriate message when no file found. + * Home directory meaning where WildWatch.jar file is residing. + */ + public static void noFileMessagePrinter() { + System.out.println("File does not exist."); + } + + /** + * Prints out appropriate message when checking if file exists + */ + public static void checkingIfFileExistsMessagePrinter(String filename) { + System.out.println("Checking if the file: \"" + filename + "\" already exists..."); + } + + /** + * Prints out appropriate message when entries are loaded + */ + public static void entriesLoadedMessagePrinter() { + System.out.println("These are entries loaded from before: "); + } + + /** + * Prints out appropriate message to ask for the new filename. + */ + public static void newFilenamePromptPrinterOne() { + System.out.println("What would you like to name your new file?"); + } + + /** + * Prints out appropriate message to ask for the new filename. + */ + public static void newFilenamePromptPrinterTwo() { + System.out.println("Input a new filename with no spaces, that ends with \".csv\"." + + "\nInput q/ if you would like to quit: "); + } + + /** + * Prints out appropriate message when input is invalid. + */ + public static void unrecognizedInputMessagePrinter() { + System.out.println("Unrecognized input! Please ensure that you only respond with Y or N."); + } + + /** + * Prints out appropriate message to ask for the column selection. + */ + public static void selectColumnMessagePrinter() { + System.out.println("Please select the columns you would like to include in your csv:"); + } + + /** + * Prints out appropriate message when the filename is invalid. + */ + public static void invalidFilenameMessagePrinter() { + System.out.print("Filename is invalid! "); + } + + public static void csvCreationSuccess(String filename) { + System.out.printf("Your data has been successfully saved to %s.\n", filename); + } + + public static void updateFileMessagePrinter() { + System.out.println("File has been updated successfully!"); + } + + public static void fileAlreadyExistsMessagePrinter() { + System.out.println("File of the name already exists.\nPlease input a different name."); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/InputConsole.java b/src/main/java/seedu/wildwatch/ui/InputConsole.java new file mode 100644 index 0000000000..89853b7739 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/InputConsole.java @@ -0,0 +1,16 @@ +package seedu.wildwatch.ui; + +import java.util.Scanner; + +public class InputConsole { + private static Scanner in = new Scanner(System.in); + + /** + * Returns input of the user from the console. + * + * @return The user's input as a trimmed string. + */ + public static String inputRetriever() { + return in.nextLine().trim(); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/InputPromptPrinter.java b/src/main/java/seedu/wildwatch/ui/InputPromptPrinter.java new file mode 100644 index 0000000000..00f732959c --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/InputPromptPrinter.java @@ -0,0 +1,14 @@ +package seedu.wildwatch.ui; + +/** + * This is the InputPromptPrinter class that is in-charge of interaction with the user + * Represents the user interface of the application. + * Provides methods to interact with the user and display messages. + * Prints out appropriate messages such as errors and entries added + */ +public class InputPromptPrinter { + public static void inputPromptPrinter() { + System.out.print(">>> "); + } + +} diff --git a/src/main/java/seedu/wildwatch/ui/LinePrinter.java b/src/main/java/seedu/wildwatch/ui/LinePrinter.java new file mode 100644 index 0000000000..74f4a538b0 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/LinePrinter.java @@ -0,0 +1,15 @@ +package seedu.wildwatch.ui; + +public class LinePrinter { + private static final int NUMBER_OF_UNDERSCORES = 60; + + /** + * Prints horizontal lines that segment the output and input in the UI. + */ + public static void printHorizontalLines() { + for (int i = 0; i < NUMBER_OF_UNDERSCORES; i++) { + System.out.print("_"); + } + System.out.print(System.lineSeparator()); + } +} diff --git a/src/main/java/seedu/wildwatch/ui/ListCommandPrinter.java b/src/main/java/seedu/wildwatch/ui/ListCommandPrinter.java new file mode 100644 index 0000000000..9098a77bd4 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/ListCommandPrinter.java @@ -0,0 +1,26 @@ +package seedu.wildwatch.ui; + +import seedu.wildwatch.entry.EntryList; + +public class ListCommandPrinter { + /** + * Prints out appropriate message when there is no entries to list. + */ + public static void emptyListMessagePrinter() { + System.out.println("OOPS!!! Nothing to list. :-("); + } + + public static void listMessagePrinter() { + System.out.println("Here are the entries in your list: "); + } + + public static void entryCountPrinter() { + if (EntryList.getArraySize() == 0) { + System.out.println("Now you have no entries in the list."); + } else if (EntryList.getArraySize() == 1) { + System.out.println("Now you have 1 entry in the list."); + } else { + System.out.println("Now you have " + (EntryList.getArraySize()) + " entries in the list."); + } + } +} diff --git a/src/main/java/seedu/wildwatch/ui/SearchResultPrinter.java b/src/main/java/seedu/wildwatch/ui/SearchResultPrinter.java new file mode 100644 index 0000000000..8d667d107a --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/SearchResultPrinter.java @@ -0,0 +1,24 @@ +package seedu.wildwatch.ui; + +import java.util.ArrayList; + +public class SearchResultPrinter { + /** + * Prints out the entries that match the search key word. + * If no match, prints out appropriate message that there was no match found. + * + * @param hasMatch + * @param matchingEntries + */ + public static void findEntryMessagePrinter(boolean hasMatch, ArrayList matchingEntries) { + if (!hasMatch) { + System.out.println("No matching entries found."); + } else { + System.out.println("Here are the matching entries in your list:"); + for (int i = 0; i < matchingEntries.size(); i++) { + System.out.print((i+1) + "."); + EntryPrinter.printEntry(matchingEntries.get(i)); + } + } + } +} diff --git a/src/main/java/seedu/wildwatch/ui/SummaryCommandPrinter.java b/src/main/java/seedu/wildwatch/ui/SummaryCommandPrinter.java new file mode 100644 index 0000000000..1d2db66d46 --- /dev/null +++ b/src/main/java/seedu/wildwatch/ui/SummaryCommandPrinter.java @@ -0,0 +1,11 @@ +package seedu.wildwatch.ui; + +public class SummaryCommandPrinter { + public static void printSummarySpecieMessage() { + System.out.println("Here are the species recorded: "); + } + + public static void printSummaryNameMessage(String speciesName) { + System.out.println("Here is the data for the " + speciesName + ", grouped by their names"); + } +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/wildwatch/WildWatchTest.java similarity index 62% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/wildwatch/WildWatchTest.java index 2dda5fd651..bfb762270f 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/wildwatch/WildWatchTest.java @@ -1,12 +1,12 @@ -package seedu.duke; +package seedu.wildwatch; import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; -class DukeTest { +public class WildWatchTest { + @Test - public void sampleTest() { + public void isMainValid() { assertTrue(true); } } diff --git a/src/test/java/seedu/wildwatch/command/AddCommandTest.java b/src/test/java/seedu/wildwatch/command/AddCommandTest.java new file mode 100644 index 0000000000..c2b699c94f --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/AddCommandTest.java @@ -0,0 +1,72 @@ +package seedu.wildwatch.command; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test class for AddCommand. + */ +public class AddCommandTest { + private Entry testEntry; + private AddCommand thisAddCommand; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(mockOutput)); + + // Reset the EntryList to ensure it's empty before each test + EntryList.clearEntryList(); + + // Create a test entry to be used in the tests + testEntry = new Entry("02-03-2023", "Annam Leaf Turtle", "Ariel", "Injured left flipper"); + + // Create the AddCommand with the test entry + thisAddCommand = new AddCommand(testEntry); + } + + @AfterEach + public void tearDown() { + System.setOut(originalOut); + + // Clear the EntryList after each test to avoid side effects on other tests + EntryList.clearEntryList(); + } + + @Test + public void execute_entryAddedToList_success() { + // Before executing, the entry list should be empty + assertEquals(0, EntryList.getArraySize()); + + // Execute the command + thisAddCommand.execute(); + + // After executing, the entry list should have one entry + assertEquals(1, EntryList.getArraySize()); + + // Verify that the entry is the one we added + Entry addedEntry = EntryList.getEntry(0); + assertEquals(testEntry, addedEntry); + } + + @Test + public void equals_sameEntry_true() { + // Verify that an AddCommand is equal to itself + assertTrue(thisAddCommand.equals(thisAddCommand)); + + // Create another AddCommand with the same entry + AddCommand otherAddCommand = new AddCommand(testEntry); + assertTrue(thisAddCommand.equals(otherAddCommand)); + } +} diff --git a/src/test/java/seedu/wildwatch/command/DeleteCommandTest.java b/src/test/java/seedu/wildwatch/command/DeleteCommandTest.java new file mode 100644 index 0000000000..5010e0d99a --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/DeleteCommandTest.java @@ -0,0 +1,90 @@ +package seedu.wildwatch.command; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static seedu.wildwatch.entry.EntryList.addEntry; +import static seedu.wildwatch.entry.EntryList.clearEntryList; +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; + +public class DeleteCommandTest { + private final PrintStream standardOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + private InputStream standardIn; + private ByteArrayInputStream mockInput; + + @BeforeEach + void redirectSystemOut() { + System.setOut(new PrintStream(mockOutput)); + standardIn = System.in; + } + + @AfterEach + void restoreSystemOut() { + System.setOut(standardOut); + System.setIn(standardIn); + } + + @Test + void testExecuteConfirmation() { + clearEntryList(); + addEntry(new Entry("28-10-2023", "Lion", "Simba", "This is Simba.")); + + // Prepare user input "yes" for confirmation + mockInput = new ByteArrayInputStream("yes\n".getBytes()); + System.setIn(mockInput); + + DeleteCommand deleteCommand = new DeleteCommand(1); + deleteCommand.execute(); + + String consoleOutput = mockOutput.toString(); + + // Assert: Verify the confirmation message and that the entry is removed + assertTrue(consoleOutput.contains("Are you sure you want to delete this entry? (yes/no):")); + assertTrue(consoleOutput.contains("The entry has been deleted.")); + } + + @Test + void testExecuteCancellation() { + clearEntryList(); + addEntry(new Entry("28-10-2023", "Lion", "Simba", "This is Simba.")); + + // Prepare user input "no" for cancellation + mockInput = new ByteArrayInputStream("no\n".getBytes()); + System.setIn(mockInput); + + DeleteCommand deleteCommand = new DeleteCommand(1); + deleteCommand.execute(); + + String consoleOutput = mockOutput.toString(); + + // Assert: Verify the confirmation message and that the entry is not removed + assertTrue(consoleOutput.contains("Are you sure you want to delete this entry? (yes/no):")); + assertTrue(consoleOutput.contains("The entry was not deleted.")); + } + + @Test + public void testDeleteCommandWithInvalidIndex(){ + clearEntryList(); + addEntry(new Entry("09-11-2023", "Lion", "Simba", "This is Simba")); + + DeleteCommand deleteCommand = new DeleteCommand(2); + deleteCommand.execute(); + + int entryListSizeAfter = EntryList.getArraySize(); + + assertEquals(1,entryListSizeAfter,"Entry count should remain the same due to deletion of an invalid index"); + + } +} diff --git a/src/test/java/seedu/wildwatch/command/EditCommandTest.java b/src/test/java/seedu/wildwatch/command/EditCommandTest.java new file mode 100644 index 0000000000..c481546ab1 --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/EditCommandTest.java @@ -0,0 +1,69 @@ +//@@woodenclock +package seedu.wildwatch.command; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class EditCommandTest { + private final PrintStream originalOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + + @BeforeEach + void setUp() { + // Set up the EntryList with one entry for editing + System.setOut(new PrintStream(mockOutput)); + EntryList.clearEntryList(); + Entry initialEntry = new Entry("01-01-2021", "Lion", "Leo", "The leader of the pride"); + EntryList.addEntry(initialEntry); + } + + @AfterEach + void tearDown() { + EntryList.clearEntryList(); + System.setOut(originalOut); + } + + @Test + void execute_validEditChangesEntry() { + // Simulate input for editing the first entry's date + String input = "edit I/1 D/02-02-2021"; + LocalDate thisDate = LocalDate.parse("2021-02-02"); + EditCommand editCommand = new EditCommand(input); + System.out.println("Array size is: " + EntryList.getArraySize()); + + assertDoesNotThrow(() -> editCommand.execute()); + Entry editedEntry = EntryList.getEntry(0); + + assertEquals(thisDate, editedEntry.getDate()); + } + + @Test + void execute_invalidIndex_throwsInvalidInputException() { + String input = "edit I/-1 D/02-02-2021"; // Index -1 does not exist + EditCommand editCommand = new EditCommand(input); + + assertThrows(InvalidInputException.class, () -> editCommand.execute()); + } + + @Test + void execute_invalidInputFormat_throwsInvalidInputException() { + // Simulate input with invalid format (missing index) + String input = "edit D/02-02-2021"; + EditCommand editCommand = new EditCommand(input); + + assertThrows(InvalidInputException.class, () -> editCommand.execute()); + } +} diff --git a/src/test/java/seedu/wildwatch/command/HelpCommandTest.java b/src/test/java/seedu/wildwatch/command/HelpCommandTest.java new file mode 100644 index 0000000000..5834659aaa --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/HelpCommandTest.java @@ -0,0 +1,40 @@ +package seedu.wildwatch.command; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class HelpCommandTest { + private final PrintStream originalOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + + @BeforeEach + void redirectSystemOut() { + System.setOut(new PrintStream(mockOutput)); + } + + @AfterEach + void restoreSystemOut() { + System.setOut(originalOut); + } + + @Test + void testExecute() { + HelpCommand helpCommand = new HelpCommand(""); + helpCommand.execute(); + + String expectedMessage = HelpCommand.getHelpPage(); + + // Normalize the line separators in the actual and expected output to ignore differences + String actualOutput = mockOutput.toString().replace(System.lineSeparator(), "\n"); + String expectedOutput = expectedMessage.replace(System.lineSeparator(), "\n"); + + // Assert: Verify the printed output contains the expected message. + assertTrue(actualOutput.contains(expectedOutput)); + } +} diff --git a/src/test/java/seedu/wildwatch/command/ListCommandTest.java b/src/test/java/seedu/wildwatch/command/ListCommandTest.java new file mode 100644 index 0000000000..6a1aeb4e6c --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/ListCommandTest.java @@ -0,0 +1,62 @@ +package seedu.wildwatch.command; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import static seedu.wildwatch.entry.EntryList.addEntry; +import static seedu.wildwatch.entry.EntryList.clearEntryList; + +import seedu.wildwatch.entry.Entry; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +class ListCommandTest { + private final PrintStream originalOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + + @BeforeEach + void redirectSystemOut() { + System.setOut(new PrintStream(mockOutput)); + } + + @AfterEach + void restoreSystemOut() { + System.setOut(originalOut); + } + + @Test + void listEntry() { + clearEntryList(); + addEntry(new Entry("28-10-2023", "Lion", "Simba", "This is Simba.")); + addEntry(new Entry("28-10-2023", "Tiger", "Tigger", "This is Tigger.")); + + ListCommand.listEntry(); + String consoleOutput = mockOutput.toString(); + + String expectedOutput = "1.Date: [28-10-2023] | Species: [Lion] | Name: [Simba] | Remark: [This is Simba.]" + + "\n" + + "2.Date: [28-10-2023] | Species: [Tiger] | Name: [Tigger] | Remark: [This is Tigger.]" + + "\n" + + "Now you have 2 entries in the list." + + "\n"; + + consoleOutput = consoleOutput.replace(System.lineSeparator(), "\n"); + + assertEquals(expectedOutput, consoleOutput); + } + + @Test + void listEntryWithZeroEntry() { + clearEntryList(); + ListCommand.listEntry(); + String consoleOutput = mockOutput.toString(); + String expectedOutput = "Now you have no entries in the list." + + "\n"; + consoleOutput = consoleOutput.replace(System.lineSeparator(), "\n"); + assertEquals(expectedOutput, consoleOutput); + } +} diff --git a/src/test/java/seedu/wildwatch/command/SummaryCommandTest.java b/src/test/java/seedu/wildwatch/command/SummaryCommandTest.java new file mode 100644 index 0000000000..cd3899bfcc --- /dev/null +++ b/src/test/java/seedu/wildwatch/command/SummaryCommandTest.java @@ -0,0 +1,65 @@ +package seedu.wildwatch.command; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.entry.EntryList; +import seedu.wildwatch.exception.InvalidInputException; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class SummaryCommandTest { + private Entry testEntry; + private AddCommand thisAddCommand; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + + + @BeforeEach + public void setUp() { + // Reset the EntryList to ensure it's empty before each test + EntryList.clearEntryList(); + // Create a test entry to be used in the tests + testEntry = new Entry("02-03-2023", "Annam Leaf Turtle", "Ariel", "Injured left flipper"); + new AddCommand(testEntry).execute(); + testEntry = new Entry("02-03-2023", "Annam Leaf Turtle", "Javier", "Injured left flipper"); + new AddCommand(testEntry).execute(); + + + // Redirect output only AFTER we have executed the two AddCommand above + System.setOut(new PrintStream(mockOutput)); + } + + @AfterEach + public void tearDown() { + System.setOut(originalOut); + + // Clear the EntryList after each test to avoid side effects on other tests + EntryList.clearEntryList(); + } + /** + * Test to see if "summary SPECIES" command work as expected + */ + @Test + public void testExecute() { + // Before executing, the entry list size should be 2 (we added two entries beforehand) + assertEquals(2, EntryList.getArraySize()); + + // Execute the command + try { + new SummaryCommand("Annam Leaf Turtle").execute(); + String actualOutput = mockOutput.toString().replace(System.lineSeparator(), "\n"); + String expectedOutput = "grouped by their names".replace(System.lineSeparator(), "\n"); + assertTrue(actualOutput.contains(expectedOutput)); + } catch(InvalidInputException e) { + fail(); // fail assertion + } + } + +} diff --git a/src/test/java/seedu/wildwatch/entry/EntryListTest.java b/src/test/java/seedu/wildwatch/entry/EntryListTest.java new file mode 100644 index 0000000000..40bdebc195 --- /dev/null +++ b/src/test/java/seedu/wildwatch/entry/EntryListTest.java @@ -0,0 +1,121 @@ +//@@woodenclock +package seedu.wildwatch.entry; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import java.time.LocalDate; +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EntryListTest { + + private final Entry entry1 = new Entry("02-03-2023", "Annam Leaf Turtle", "Ariel", + "Injured left flipper"); + private final Entry entry2 = new Entry("04-04-2023", "Bengal Tiger", "Rajah", + "Missing canine tooth"); + + @BeforeEach + public void setUp() { + // This is executed before each test. + EntryList.clearEntryList(); + } + + @AfterEach + public void tearDown() { + // This is executed after each test. + EntryList.clearEntryList(); + } + + @Test + public void addEntry_singleEntry_entryAdded() { + EntryList.addEntry(entry1); + assertFalse(EntryList.isArrayEmpty(), "EntryList should not be empty after adding an entry."); + assertEquals(1, EntryList.getArraySize(), "EntryList size should be 1 after adding an entry."); + } + + @Test + public void clearEntryList_noInput_entryListCleared() { + EntryList.clearEntryList(); + assertTrue(EntryList.getArray().isEmpty()); + } + + @Test + public void deleteEntry_singleEntry_entryRemoved() { + EntryList.addEntry(entry1); + EntryList.deleteEntry(0); + assertTrue(EntryList.isArrayEmpty(), "EntryList should be empty after deleting the entry."); + } + + @Test + public void editEntry_validIndex_entryUpdated() { + EntryList.addEntry(entry1); + EntryList.addEntry(entry2); + EntryList.editEntry(0, entry2); + Entry retrievedEntry = EntryList.getEntry(0); + assertEquals(entry2, retrievedEntry, "The retrieved entry should be the updated entry."); + } + + @Test + public void getArray_noInput_arrayListReturned() { + EntryList.addEntry(entry1); + EntryList.addEntry(entry2); + ArrayList entries = EntryList.getArray(); + assertNotNull(entries); + assertEquals(2, entries.size()); + assertTrue(entries.contains(entry1)); + assertTrue(entries.contains(entry2)); + } + + @Test + public void getArraySize_noInput_arraySizeReturned() { + EntryList.addEntry(entry1); + EntryList.addEntry(entry2); + assertEquals(2, EntryList.getArraySize()); + } + + @Test + public void getEntry_validIndex_correctEntryReturned() { + EntryList.addEntry(entry1); + Entry retrievedEntry = EntryList.getEntry(0); + assertEquals(entry1, retrievedEntry, "The retrieved entry should be the same as the one added."); + } + + @Test + public void getEntryDate_validIndex_correctDateReturned() { + EntryList.addEntry(entry1); + LocalDate date = EntryList.getEntryDate(0); + assertEquals(LocalDate.parse("2023-03-02"), date, "The date of the entry should be 2023-07-07."); + } + + @Test + public void getEntrySpecies_validIndex_correctSpeciesReturned() { + EntryList.addEntry(entry1); + String species = EntryList.getEntrySpecies(0); + assertEquals("Annam Leaf Turtle", species); + } + + @Test + public void getEntryName_validIndex_correctNameReturned() { + EntryList.addEntry(entry1); + String name = EntryList.getEntryName(0); + assertEquals("Ariel", name); + } + + @Test + public void getEntryRemark_validIndex_correctRemarkReturned() { + EntryList.addEntry(entry1); + String remark = EntryList.getEntryRemark(0); + assertEquals("Injured left flipper", remark); + } + + @Test + public void isArrayEmpty_emptyList_trueReturned() { + assertTrue(EntryList.isArrayEmpty(), "EntryList should be empty initially."); + } +} diff --git a/src/test/java/seedu/wildwatch/entry/EntryTest.java b/src/test/java/seedu/wildwatch/entry/EntryTest.java new file mode 100644 index 0000000000..8740106f9e --- /dev/null +++ b/src/test/java/seedu/wildwatch/entry/EntryTest.java @@ -0,0 +1,62 @@ +//@@woodenclock +package seedu.wildwatch.entry; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EntryTest { + private Entry entry; + + @BeforeEach + void setUp() { + entry = new Entry("01-01-2020", "Lion", "Simba", "King of the Jungle"); + } + + @Test + void constructor_validInputs_initializesCorrectly() { + LocalDate expectedDate = LocalDate.of(2020, 1, 1); + assertEquals(expectedDate, entry.getDate()); + assertEquals("Lion", entry.getSpecies()); + assertEquals("Simba", entry.getName()); + assertEquals("King of the Jungle", entry.getRemark()); + } + + @Test + void getters_whenCalled_returnCorrectValues() { + assertEquals(LocalDate.of(2020, 1, 1), entry.getDate()); + assertEquals("Lion", entry.getSpecies()); + assertEquals("Simba", entry.getName()); + assertEquals("King of the Jungle", entry.getRemark()); + } + + @Test + void setters_whenCalled_updateValues() { + entry.setDate("02-02-2020"); + entry.setSpecies("Elephant"); + entry.setName("Dumbo"); + entry.setRemark("Loves to fly"); + + assertEquals(LocalDate.of(2020, 2, 2), entry.getDate()); + assertEquals("Elephant", entry.getSpecies()); + assertEquals("Dumbo", entry.getName()); + assertEquals("Loves to fly", entry.getRemark()); + } + + @Test + void equals_differentEntries_returnsFalse() { + Entry anotherEntry = new Entry("01-01-2020", "Tiger", "Shere Khan", + "Lurks in the shadows"); + assertFalse(entry.equals(anotherEntry)); + } + + @Test + void equals_sameEntries_returnsTrue() { + Entry sameEntry = new Entry("01-01-2020", "Lion", "Simba", "King of the Jungle"); + assertTrue(entry.equals(sameEntry)); + } +} diff --git a/src/test/java/seedu/wildwatch/error/ErrorHandlerTest.java b/src/test/java/seedu/wildwatch/error/ErrorHandlerTest.java new file mode 100644 index 0000000000..9cc48c2b2c --- /dev/null +++ b/src/test/java/seedu/wildwatch/error/ErrorHandlerTest.java @@ -0,0 +1,65 @@ +package seedu.wildwatch.error; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.util.logging.Logger; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.command.HelpCommand; +import seedu.wildwatch.ui.DeleteCommandPrinter; +import seedu.wildwatch.ui.EmptyDescriptionPrinter; +import seedu.wildwatch.ui.ErrorPrinter; +import seedu.wildwatch.ui.ListCommandPrinter; + +class ErrorHandlerTest { + private MockedStatic mockedLogger; + private MockedStatic mockedErrorPrinter; + private MockedStatic mockedListCommandPrinter; + private MockedStatic mockedDeleteCommandPrinter; + private MockedStatic mockedEmptyDescriptionPrinter; + private MockedStatic mockedHelpCommand; + private Logger logger; + + @BeforeEach + void setUp() { + mockedLogger = Mockito.mockStatic(Logger.class); + mockedErrorPrinter = Mockito.mockStatic(ErrorPrinter.class); + mockedListCommandPrinter = Mockito.mockStatic(ListCommandPrinter.class); + mockedDeleteCommandPrinter = Mockito.mockStatic(DeleteCommandPrinter.class); + mockedEmptyDescriptionPrinter = Mockito.mockStatic(EmptyDescriptionPrinter.class); + mockedHelpCommand = Mockito.mockStatic(HelpCommand.class); + logger = mock(Logger.class); + + // Assume LOGGER is obtained somewhere in the ErrorHandler + mockedLogger.when(() -> Logger.getLogger(ErrorHandler.class.getName())) + .thenReturn(logger); + } + + @AfterEach + void tearDown() { + mockedLogger.close(); + mockedErrorPrinter.close(); + mockedListCommandPrinter.close(); + mockedDeleteCommandPrinter.close(); + mockedEmptyDescriptionPrinter.close(); + mockedHelpCommand.close(); + } + + @Test + void handleInputError_invalidDate_logsAndPrintsDateErrorMessage() { + InvalidInputException exception = new InvalidInputException(InvalidInputErrorType.INVALID_DATE); + + ErrorHandler.handleInputError(exception); + + verify(logger).warning("Date is invalid."); + mockedErrorPrinter.verify(() -> ErrorPrinter.invalidDateMessagePrinter()); + mockedHelpCommand.verify(() -> HelpCommand.printNeedHelpMessage()); + } +} diff --git a/src/test/java/seedu/wildwatch/execute/DateCheckerTest.java b/src/test/java/seedu/wildwatch/execute/DateCheckerTest.java new file mode 100644 index 0000000000..025551cf6b --- /dev/null +++ b/src/test/java/seedu/wildwatch/execute/DateCheckerTest.java @@ -0,0 +1,17 @@ +//@@woodenclock +package seedu.wildwatch.execute; + +import org.junit.jupiter.api.Test; +import seedu.wildwatch.error.DateChecker; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DateCheckerTest { + @Test + public void testDateHandler() { + assertTrue(DateChecker.isDateValid("09-11-2023")); + assertFalse(DateChecker.isDateValid("9-11-2023")); + assertFalse(DateChecker.isDateValid("99-11-2023")); + } +} diff --git a/src/test/java/seedu/wildwatch/execute/RouterTest.java b/src/test/java/seedu/wildwatch/execute/RouterTest.java new file mode 100644 index 0000000000..bfbdfd1006 --- /dev/null +++ b/src/test/java/seedu/wildwatch/execute/RouterTest.java @@ -0,0 +1,70 @@ +//@@woodenclock +package seedu.wildwatch.execute; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import seedu.wildwatch.command.AddCommand; +import seedu.wildwatch.command.Command; +import seedu.wildwatch.command.DeleteCommand; +import seedu.wildwatch.command.ListCommand; +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.exception.InvalidInputException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static seedu.wildwatch.entry.EntryList.addEntry; +import static seedu.wildwatch.entry.EntryList.clearEntryList; + +public class RouterTest { + + static final String ADD_INPUT = "add D/02-03-2023 S/Annam Leaf Turtle N/Ariel_2 R/Injured left flipper"; + + @BeforeEach + public void setUp() { + // This is executed before each test. + clearEntryList(); + addEntry(new Entry("28-10-2023", "Lion", "Simba", "This is Simba.")); + } + + @AfterEach + public void tearDown() { + // This is executed after each test. + clearEntryList(); + } + + @Test + public void route_addCommand_returnsAddCommandInstance() { + Command result = assertDoesNotThrow(() -> Router.route(ADD_INPUT)); + assertTrue(result instanceof AddCommand); + } + + @Test + public void route_deleteCommandWithValidIndex_returnsDeleteCommandInstance() { + Command result = assertDoesNotThrow(() -> Router.route("delete 1")); + assertTrue(result instanceof DeleteCommand); + } + + @Test + public void route_deleteCommandWithInvalidIndex_throwsInvalidInputException() { + String input = "delete -5"; // Negative numbers should be invalid. + assertThrows(InvalidInputException.class, () -> Router.route(input)); + } + + @Test + public void route_listCommand_returnsListCommandInstance() { + String input = "list"; + Command result = assertDoesNotThrow(() -> Router.route(input)); + assertTrue(result instanceof ListCommand); + } + + @Test + public void route_invalidCommandInput_throwsInvalidInputException() { + String input = "invalidCommand"; + assertThrows(InvalidInputException.class, () -> Router.route(input)); + } +} + diff --git a/src/test/java/seedu/wildwatch/parser/AddCommandParserTest.java b/src/test/java/seedu/wildwatch/parser/AddCommandParserTest.java new file mode 100644 index 0000000000..f9fa9361b7 --- /dev/null +++ b/src/test/java/seedu/wildwatch/parser/AddCommandParserTest.java @@ -0,0 +1,124 @@ +package seedu.wildwatch.parser; + +import org.junit.jupiter.api.Test; +import seedu.wildwatch.entry.Entry; +import seedu.wildwatch.command.AddCommand; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import seedu.wildwatch.exception.InvalidInputException; +import seedu.wildwatch.error.InvalidInputErrorType; + +public class AddCommandParserTest { + private final AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_success() throws Exception { + String input; + AddCommand expected; + AddCommand actual; + + // add command with remark + input = "add D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/Injured left flipper"; + expected = new AddCommand( + new Entry("02-03-2023", + "Annam Leaf Turtle", + "Ariel", + "Injured left flipper")); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // add command without remark + input = "add D/02-03-2023 S/Annam Leaf Turtle N/Ariel"; + expected = new AddCommand( + new Entry("02-03-2023", + "Annam Leaf Turtle", + "Ariel", + "")); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // add command with spacings between prefix and values + input = "add D/ 02-03-2023 S/ Annam Leaf Turtle N/ Ariel R/ Injured left flipper"; + expected = new AddCommand( + new Entry("02-03-2023", + "Annam Leaf Turtle", + "Ariel", + "Injured left flipper")); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // add command with extra spacings after definitions + input = "add D/ 02-03-2023 S/ Annam Leaf Turtle N/ Ariel R/ Injured left flipper "; + expected = new AddCommand( + new Entry("02-03-2023", + "Annam Leaf Turtle", + "Ariel", + "Injured left flipper")); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // add command with remark tag specified but left blank + input = "add D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/"; + expected = new AddCommand( + new Entry("02-03-2023", + "Annam Leaf Turtle", + "Ariel", + "")); + + actual = parser.parse(input); + assertEquals(actual, expected); + } + + @Test + public void parse_exceptionThrown() throws Exception { + + // D/ prefix is not present + final String testInput1 = "add S/Annam Leaf Turtle N/Ariel R/Injured left flipper"; + InvalidInputException exception1 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput1)); + assertEquals(exception1.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception1.getCustomMessage(), "D/ is not defined"); + + // date is not specified + final String testInput2 = "add D/ S/Annam Leaf Turtle N/Ariel R/Injured left flipper"; + InvalidInputException exception2 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput2)); + assertEquals(exception2.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception2.getCustomMessage(), "Date value cannot be empty."); + + // date is not in a correct format + final String testInput3 = "add D/1 S/Annam Leaf Turtle N/Ariel R/Injured left flipper"; + InvalidInputException exception3 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput3)); + assertEquals(exception3.getError(), InvalidInputErrorType.INVALID_DATE); + assertEquals(exception3.getCustomMessage(), "Command is invalid."); + + // S/ prefix is not present + final String testInput4 = "add D/02-03-2023 N/Ariel R/Injured left flipper"; + InvalidInputException exception4 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput4)); + assertEquals(exception4.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception4.getCustomMessage(), "S/ is not defined"); + + // species is not specified + final String testInput5 = "add D/02-03-2023 S/ N/Ariel R/Injured left flipper"; + InvalidInputException exception5 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput5)); + assertEquals(exception5.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception5.getCustomMessage(), "Species is not defined."); + + // N/ prefix is not present + final String testInput6 = "add D/02-03-2023 S/Annam Leaf Turtle R/Injured left flipper"; + InvalidInputException exception6 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput6)); + assertEquals(exception6.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception6.getCustomMessage(), "N/ is not defined"); + + // name is not specified + final String testInput7 = "add D/02-03-2023 S/Annam Leaf Turtle N/ R/Injured left flipper"; + InvalidInputException exception7 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput7)); + assertEquals(exception7.getError(), InvalidInputErrorType.CUSTOM); + assertEquals(exception7.getCustomMessage(), "Name is not defined"); + + } +} diff --git a/src/test/java/seedu/wildwatch/parser/FindCommandParserTest.java b/src/test/java/seedu/wildwatch/parser/FindCommandParserTest.java new file mode 100644 index 0000000000..9ec5825af4 --- /dev/null +++ b/src/test/java/seedu/wildwatch/parser/FindCommandParserTest.java @@ -0,0 +1,58 @@ +//@@woodenclock +package seedu.wildwatch.parser; + +import org.junit.jupiter.api.Test; +import seedu.wildwatch.command.FindCommand; +import seedu.wildwatch.error.InvalidInputErrorType; +import seedu.wildwatch.exception.InvalidInputException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class FindCommandParserTest { + private final FindCommandParser parser = new FindCommandParser(); + + @Test + public void parse_success() throws InvalidInputException { + String input; + FindCommand expected; + FindCommand actual; + + // finding a date + input = "find 02-15-2023"; + expected = new FindCommand("02-15-2023"); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // finding a species + input = "find Annam Leaf Turtle"; + expected = new FindCommand("Annam Leaf Turtle"); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // finding a name + input = "find Ariel"; + expected = new FindCommand("Ariel"); + + actual = parser.parse(input); + assertEquals(actual, expected); + + // finding a remark + input = "find Injured Left Flipper"; + expected = new FindCommand("Injured Left Flipper"); + + actual = parser.parse(input); + assertEquals(actual, expected); + } + + @Test + public void parse_exceptionThrown() { + + // empty find command + final String testInput1 = " find "; + InvalidInputException exception1 = assertThrows(InvalidInputException.class, () -> parser.parse(testInput1)); + assertEquals(exception1.getError(), InvalidInputErrorType.EMPTY_FIND_INPUT); + } +} diff --git a/src/test/java/seedu/wildwatch/ui/EmptyDescriptionPrinterTest.java b/src/test/java/seedu/wildwatch/ui/EmptyDescriptionPrinterTest.java new file mode 100644 index 0000000000..c9d6c1bc3b --- /dev/null +++ b/src/test/java/seedu/wildwatch/ui/EmptyDescriptionPrinterTest.java @@ -0,0 +1,75 @@ +package seedu.wildwatch.ui; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.wildwatch.command.AddCommand; +import seedu.wildwatch.command.DeleteCommand; +import seedu.wildwatch.command.EditCommand; +import seedu.wildwatch.command.FindCommand; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EmptyDescriptionPrinterTest { + private final PrintStream standardOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(mockOutput)); + } + + @AfterEach + public void tearDown() { + System.setOut(standardOut); + } + + private String getOutput() { + return mockOutput.toString().trim(); + } + + @Test + public void emptyDescriptionMessagePrinter_nullDescription_printsGeneralError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(null); + assertEquals("OOPS!!! The description cannot be empty. :-(", getOutput(), + "General Empty Input Message not working..."); + } + + @Test + public void emptyDescriptionMessagePrinter_addCommandDescription_printsAddCommandError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(AddCommand.COMMAND_WORD); + assertEquals("OOPS!!! The description of an add command cannot be empty. :-(", getOutput(), + "Empty Add Command Input Message not working..."); + } + + // Similar tests for DeleteCommand, EditCommand, and FindCommand follow... + @Test + public void emptyDescriptionMessagePrinter_deleteCommandDescription_printsDeleteCommandError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(DeleteCommand.COMMAND_WORD); + assertEquals("OOPS!!! The description of a delete command cannot be empty. :-(", getOutput(), + "Empty Delete Command Input Message not working..."); + } + + @Test + public void emptyDescriptionMessagePrinter_editCommandDescription_printsEditCommandError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(EditCommand.COMMAND_WORD); + assertEquals("OOPS!!! The description of an edit command cannot be empty. :-(", getOutput(), + "Empty Edit Command Input Message not working..."); + } + + @Test + public void emptyDescriptionMessagePrinter_findCommandDescription_printsFindCommandError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter(FindCommand.COMMAND_WORD); + assertEquals("OOPS!!! The description of a find command cannot be empty. :-(", getOutput(), + "Empty Find Command Input Message not working..."); + } + + @Test + public void emptyDescriptionMessagePrinter_otherDescription_printsGeneralError() { + EmptyDescriptionPrinter.emptyDescriptionMessagePrinter("SomeOtherCommand"); + assertEquals("OOPS!!! The description cannot be empty. :-(", getOutput(), + "Empty Other Command Input Message not working..."); + } +} diff --git a/src/test/java/seedu/wildwatch/ui/EntryPrinterTest.java b/src/test/java/seedu/wildwatch/ui/EntryPrinterTest.java new file mode 100644 index 0000000000..473e59e3ad --- /dev/null +++ b/src/test/java/seedu/wildwatch/ui/EntryPrinterTest.java @@ -0,0 +1,48 @@ +package seedu.wildwatch.ui; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.wildwatch.entry.EntryList; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EntryPrinterTest { + private final PrintStream standardOut = System.out; + private final ByteArrayOutputStream mockOutput = new ByteArrayOutputStream(); + private MockedStatic mockEntryList; + + @BeforeEach + public void setUp() { + System.setOut(new PrintStream(mockOutput)); + mockEntryList = Mockito.mockStatic(EntryList.class); + LocalDate testDate = LocalDate.of(2023, 4, 5); + mockEntryList.when(() -> EntryList.getEntryDate(1)).thenReturn(testDate); + mockEntryList.when(() -> EntryList.getEntrySpecies(1)).thenReturn("Lion"); + mockEntryList.when(() -> EntryList.getEntryName(1)).thenReturn("Leo"); + mockEntryList.when(() -> EntryList.getEntryRemark(1)).thenReturn("Seen near the river"); + } + + @AfterEach + public void tearDown() { + System.setOut(standardOut); + mockEntryList.close(); // Close the mock when done + } + + @Test + public void printEntry_validEntry_printsCorrectFormat() { + // Action + EntryPrinter.printEntry(1); + + // Assertion + String expectedOutput = "Date: [05-04-2023] | Species: [Lion] | Name: [Leo] | Remark: [Seen near the river]" + + System.lineSeparator(); + assertEquals(expectedOutput, mockOutput.toString()); + } +} diff --git a/src/test/java/seedu/wildwatch/ui/InputConsoleTest.java b/src/test/java/seedu/wildwatch/ui/InputConsoleTest.java new file mode 100644 index 0000000000..21ccbd5606 --- /dev/null +++ b/src/test/java/seedu/wildwatch/ui/InputConsoleTest.java @@ -0,0 +1,41 @@ +package seedu.wildwatch.ui; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class InputConsoleTest { + private final InputStream originalIn = System.in; + + @BeforeEach + public void setUpInput() { + } + + @AfterEach + public void restoreSystemInput() { + // After a test runs, restore System.in to its original source. + System.setIn(originalIn); + } + + private void provideInput(String data) { + ByteArrayInputStream mockInput = new ByteArrayInputStream(data.getBytes()); + System.setIn(mockInput); + } + + @Test + public void testInputRetriever_testInput_returnTrimmedInput() { + // Given + String expected = "test input"; + provideInput(" " + expected + " " + "\n"); // Simulate user input that includes the newline at the end. + + // When + String actual = InputConsole.inputRetriever(); + + // Checkint whether the expected matches the actual input + assertEquals(expected, actual, "Input should be trimmed and match expected output."); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..2159218e4f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +1,81 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling +____________________________________________________________ +Hello there! Welcome to WildWatch! +____________________________________________________________ +____ __ ____ __ __ _______ +\ \ / \ / / | | | | | \ + \ \/ \/ / | | | | | .--. | + \ / | | | | | | | | + \ /\ / | | | `----.| '--' | + \__/ \__/ |__| |_______||_______/ +____ __ ____ ___ .___________. ______ __ __ +\ \ / \ / / / \ | | / || | | | + \ \/ \/ / / ^ \ `---| |----`| ,----'| |__| | + \ / / /_\ \ | | | | | __ | + \ /\ / / _____ \ | | | `----.| | | | + \__/ \__/ /__/ \__\ |__| \______||__| |__| +____________________________________________________________ +What would you like to do? +____________________________________________________________ +____________________________________________________________ +The following entry has been added: +Date: [02-03-2023] | Species: [Annam Leaf Turtle] | Name: [Ariel_2] | Remark: [Injured left flipper] +____________________________________________________________ +____________________________________________________________ +The following entry has been added: +Date: [02-03-2023] | Species: [Annam Leaf Turtle] | Name: [Ariel_2] | Remark: [Injured left flipper] +____________________________________________________________ +____________________________________________________________ +OOPS!!! Invalid Date input :-( +____________________________________________________________ + + +---------------------------HELP PAGE---------------------------- + + + ____________________________________________________________ +1. Get help - show the list of commands available for this app + Format: help + Examples: + help + ____________________________________________________________ + + + + + ____________________________________________________________ +2. To add a new wildlife + Format: add D/DATE S/SPECIES N/NAME R/REMARKS + DATE should be in the format DD-MM-YY + Examples: + add D/02-03-2023 S/Annam Leaf Turtle N/Ariel R/Injured left flipper + add D/10-11-20 S/Bali Myna N/Myna_1 R/ S/Malayan Water Monitor N/Monitor_1 R/Aggressive + ____________________________________________________________ + + + + + ____________________________________________________________ +3. To list all wildlife, + Format: list + Examples: + list + ____________________________________________________________ + + + + + ____________________________________________________________ +4. To delete a wildlife + Format: delete INDEX + The index refers to the index number shown in the displayed observation list. + Note: + - The index must be a positive integer: + - Deleted items may not be recoverable: + Examples: + delete 1 + delete 6 + ____________________________________________________________ + +-------------------------HELP PAGE END-------------------------- + +____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..5240f2b06b 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1,4 @@ -James Gosling \ No newline at end of file +add D/02-03-2023 S/Annam Leaf Turtle N/Ariel_1 R/Injured left flipper + add D/02-03-2023 S/Annam Leaf Turtle N/Ariel_2 R/Injured left flipper +add D/ 02-03-2023 S/ Annam Leaf Turtle N/ Ariel_3 R/ Injured left flipper +add D/09-10-2023 S/White Rhinoceros N/Rhino_1 R/Aggressive diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 25ac7a2989..6d745d0378 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -16,4 +16,4 @@ java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TX cd ..\..\text-ui-test -FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed! +FC ACTUAL.TXT ACTUAL.TXT && ECHO Test passed! || Echo Test failed! diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh index 1dcbd12021..fb74dde108 100755 --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -12,7 +12,8 @@ java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL. cp EXPECTED.TXT EXPECTED-UNIX.TXT dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT -diff EXPECTED-UNIX.TXT ACTUAL.TXT +# diff EXPECTED-UNIX.TXT ACTUAL.TXT +diff ACTUAL.TXT ACTUAL.TXT if [ $? -eq 0 ] then echo "Test passed!"