Uniflow is a command-line application designed to help students manage their university modules, timetables, grades, and course reviews. The application follows an object-oriented design with clear separation of concerns.

The architecture diagram above shows the high-level design of the application. The main components are:
Uniflow (Main Class)
getScoreManager()) to commands.UI (User Interface)
Parser
Command objectsUniflowException for invalid commandsCommand (Abstract Class)
execute() method and isExit() to control application flowModule
ModuleList
Course
CourseRecord
GradeStorage
data/grades.txt file.ReviewManager
ReviewStorage
data/reviews.txt file.ExitSaveManager
bye command) and prompts the user (yes/no) to persist them.RatingManager
RatingStats
RatingStorage
ScoreManager
<MODULE_CODE: {component: value}> and persistence.ScoreStorage
The following workflow diagram illustrates the complete command execution flow in Uniflow:

Workflow Steps:
UI component reads the input from the console.Command object
It performs syntax validation and throws a UniflowException for invalid commands.execute() method is called with core data managers:
ModuleList - timetable and active semester modules.CourseRecord - completed courses for GPA tracking.ReviewManager, RatingManager, and ScoreManager - in-memory feature managers.insert, delete, list, filter, show timetable, reset timetable and clash detection.addgrade, gpa, and related grade persistence through GradeStorage.addreview, review, findreview, and review persistence through ReviewStorage.rate (add/view module ratings), persistence through RatingStorage.score (add/view module breakdowns) persistence through ScoreStorage.ModuleStorage for timetable data.GradeStorage for GPA records.ScoreStorage, ReviewStorage, RatingStorage for feature-specific data.bye.
When Command.isExit() returns true, the main loop terminates.bye.
When Command.isExit() returns true, the main loop invokes the ExitSaveManager.ExitSaveManager checks for unsaved reviews by comparing memory (ReviewManager) to the file (ReviewStorage).
Every user action (e.g., insert, delete, filter, rate, score, addgrade, review) is implemented as a subclass of the abstract Command class.
Each command encapsulates its own logic and implements execute(), allowing new features to be added without modifying existing code.
This pattern isolates user actions, improving extensibility and testability.
The system is divided into three logical layers:
ReviewManager, RatingManager, ScoreManager).ReviewStorage, RatingStorage, ScoreStorage, ModuleStorage, GradeStorage).
This design ensures that UI, logic, and persistence can evolve independently.Each major feature has a dedicated storage class that implements standardized load/save operations using simple delimited formats.
This keeps data handling consistent across modules (e.g., adata/modules.txt, data/scores.txt, data/ratings.txt).
Global components a(ReviewManager, RatingManager, ScoreManager, CourseRecord, and ModuleList) are instantiated once and referenced through the Uniflow class.
This ensures a consistent shared state across commands while avoiding tight coupling.
ModuleList uses Java’s Predicate functional interface for dynamic filtering (e.g., by day, session type, or tutorial presence).
This makes it easy to extend filtering criteria without changing the base logic.
The review system operates in a “RAM-first” model. All changes (addreview, editreview, deletereview) modify data in memory only. This improves performance by eliminating disk I/O on every command.
Persistence to disk is now explicitly controlled by the user via:
load reviews command (which prompts to merge changes).add reviews database command (for a manual merge/save).ExitSaveManager (which prompts to save on graceful exit).
The Command component uses the Command Pattern to encapsulate each user action as an object. This design allows for:
All command classes inherit from the abstract Command class and implement the execute() method. This includes new commands such as FindReview, CountReviewsCommand, LoadReviewsCommand, ResetReviewsCommand, AddReviewsDatabaseCommand, ShowTimetableCommand, and ResetTimetableCommand.

The Model component consists of:
The use of composition relationships allows ModuleList and CourseRecord to fully manage their respective collections.
The Review Management component was refactored to a “RAM-first” architecture, separating in-memory logic from user-controlled persistence.

Map of reviews. It no longer automatically saves on add, edit, or delete. It provides methods like getAllReviews() and loadReviews() for persistence commands. It includes a fallback to “RAM-only” mode if the file is unreadable.reviews.txt file).LoadReviewsCommand and AddReviewsDatabaseCommand orchestrate the merging of data between ReviewManager and ReviewStorage.ReviewManager and ReviewStorage to detect unsaved changes and prompt the user (yes/no) before the application closes via the bye command.The Rating Management component enables users to rate modules and view average ratings for each course. It follows the same architecture as Review Management, with a clear separation between logic, data and persistence layers.

Map<String, RatingStats> mapping module codes to their cumulative rating data.
sum: total of all rating values.count: number of ratings.average: computed dynamically as sum/countdata/ratings.txt using a pipe-delimited format.
MODULE_CODE|SUM|COUNT
The Score Management component allows users to store and view assessment breakdowns for individual modules.
It is designed around the same modular architecture as the Review and Rating systems.

Map<String, Map<String, Integer>> representing each module’s breakdown,
where the key is a component name (e.g., "exam", "project") and the value is a score weight.
Provides methods to:
data/scores.txt in the following format:
MODULE_CODE|name:value|name2:value2|..
Handles both loading and saving, ensuring file data remains synchronized with the in-memory state.The Rating Management component enables users to rate modules and view average ratings for each course. It follows the same architecture as Review Management, with a clear separation between logic, data and persistence layers.

Map<String, RatingStats> mapping module codes to their cumulative rating data.
sum: total of all rating values.count: number of ratings.average: computed dynamically as sum/countdata/ratings.txt using a pipe-delimited format.
MODULE_CODE|SUM|COUNT
The Score Management component allows users to store and view assessment breakdowns for individual modules.
It is designed around the same modular architecture as the Review and Rating systems.

Map<String, Map<String, Integer>> representing each module’s breakdown,
where the key is a component name (e.g., "exam", "project") and the value is a score weight.
Provides methods to:
data/scores.txt in the following format:
MODULE_CODE|name:value|name2:value2|..
Handles both loading and saving, ensuring file data remains synchronized with the in-memory state.The Grade Management component allows users to record, display their academic grades, and compute their GPA.
It combines the use of the CourseRecord and GradeStorage classes for GPA calculation, temporary grade testing, and grade persistence.
CourseRecord: Two lists of CourseRecord in Uniflow— one for confirmed grades (will be saved) and one for temporary predicted grades used in projection. Responsible for computing GPA.
Remark: tempRecord in Uniflow.java stored the predicted grades temporarily.
GradeStorage: Handles loading and saving of grade record in data/grades.txt, ensuring persistence between sessions.
EC3322 | 5 | B | 1Some of the Commands Implemented:
AddGradeCommand – Adds a completed course’s grade.
ShowGradeCommand – Displays all saved grades.
RemoveGradeCommand – Deletes a grade record.
AddTestGradeCommand, RemoveTestGradeCommand – Manage predicted grades and projected GPA.
ProjectGpaCommand – Combines attained grades and predicted grades to calculate projected GPA.
Design Rationale: Separate handling for predicted grades allows users to explore a projected GPA simulation safely without overwriting confirmed academic record.
The find review feature allows users to search the in-memory review list.
FindReview command with the user and/or course arguments.reviewManager.getReviews() or reviewManager.getAllCourseIds() to fetch the current in-memory data.UI for display.To prevent data loss on graceful exit, a user-prompted save mechanism is implemented, replacing the previous automatic shutdown hook.
bye, the main loop in Uniflow.java detects c.isExit() == true.ExitSaveManager and calls promptSaveBeforeExit().ExitSaveManager loads both file data (ReviewStorage.load()) and memory data (reviewManager.getAllReviews()).UI to ask the user: “You have unsaved reviews… (yes/no)”.LoadReviewsCommand (detectUnsavedReviews, mergeAndSaveReviews) to avoid code duplication.The commands for managing the review state were updated for the “RAM-first” model.
LoadReviewsCommand: This command (load reviews) first checks for unsaved in-memory changes (by comparing memory to the file).
ReviewManager and reloads all reviews from ReviewStorage.ResetReviewsCommand: This command (reset all reviews) now only affects memory.
reviewManager.clearAll(), wiping the in-memory review list.data/reviews.txt file, allowing users to clear their session for testing.A new command was added for explicit, user-controlled synchronization.
AddReviewsDatabaseCommand: This command (add reviews database) provides a way to manually save progress.
CountReviewsCommand: This new command (amount reviews) allows the user to count the number of reviews currently loaded in memory.
findreview to support counting by user (u/USER), by course (c/COURSE), or both.ShowTimetableCommand (show timetable) was implemented to provide a clean, formatted view of all modules.ModuleList and uses UI to display all module details, including ID, name, day, time, and session type, offering a more readable alternative to the list command.ResetTimetableCommand (reset timetable) was implemented to clear all modules from the ModuleList.modules.clear() and provides user feedback via the UI.The insert module feature allows users to add modules to their timetable with automatic clash detection.

How the insert feature works:
findClash()The clash detection algorithm compares time ranges on the same day using Java’s LocalTime class to determine if any overlap exists.
The filter feature allows users to search modules using various criteria.

How filtering works:
filter type and valuefilter method on ModuleListThe application supports filtering by: day, session type, module ID, module name, and tutorial presence. The predicate-based approach allows for flexible and extensible filtering logic.
The GPA calculation feature computes the cumulative GPA based on completed courses.

How GPA calculation works:
gpa command will call the computeGpa() method in CourseRecord.The grade point conversion follows the standard NUS grading scale.
The projected GPA feature allows users to simulate their projected GPA result based on predicted grades of future courses.
How it works:
addtestgrade c/COURSE_CODE cr/NUMBER_OF_CREDITS g/GRADE m/IS_MAJOR.projectgpa command combined the courses from permanent and temporary records and compute a projected GPA.This feature helps students plan ahead and understand how their upcoming courses’ performances impact their GPA.
The temporary record will not be saved as permanant record and is just for testing in this execution.
The following commands are used to facilitate the management and organisation of the course record.
showgrade – Displays the completed courses and allows users to track what is stored.
removegrade INDEX – Deletes a course grade from the permanent record by user-specified index.
showtempgrade – Displays the predicted grades currently stored in a temporary record, allowing users to track their current inputs.
removetempgrade INDEX – Deletes a predicted grade from the temporary record.
Input Validation: All command inputs will be checked to ensure validity. Invalid input will cause a UniflowException that displays a reminder to users.
Stores per-module assessment breakdowns like exam:50 project:30

How the score feature works:
score <MODULE_CODE> [name:value ...].
: present, numeric non-negative values, whitespace/commas normalized.ModuleList (module-centric model).Map<String,Integer>.scoreBreakdownScoreManager, which writes the full map to disk using ScoreStorage.ScoreManager to hydrate and display.The rating feature allows users to rate modules they’ve taken and view the average rating for each module.

How it works:
RateCommand when the user enters rate <MODULE_CODE> [RATING]RateCommand validates that the course exists in CourseRecordRatingManager, which updates the total and countRatingStorage saves the updated data to a file (data/ratings.txt) retrieves the average rating and rating count from RatingManager`When adding a new module, the system checks for scheduling conflicts:
checkOverlap()!(end1 < start2) AND !(end2 < start1)This prevents students from accidentally scheduling multiple classes at the same time.
Uniflow is designed for university students who:
Uniflow solves several problems for university students:
Timetable Management: Students can quickly add modules with specific time slots and session types, with automatic clash detection to prevent scheduling conflicts.
Academic Tracking: The application allows students to record completed courses with grades and automatically calculates cumulative GPA, helping them monitor academic progress.
Module Organization: Students can filter and search modules by various criteria (day, session type, module code, name), making it easy to find specific classes or plan study schedules.
Peer Reviews: The review system enables students to share and read course experiences, helping with module selection decisions.
Score Breakdown: Students can store and retrieve component scores for transparency on assessment structure.
Course Ratings: A lightweight rating system that lets students rate modules and view average ratings, providing a quick and quantitative signal alongside reviews.
Efficiency: Command-line interface allows for faster data entry compared to GUI applications, ideal for students who need to quickly update their schedules between classes.
| 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 |
| v1.0 | student | add modules to my timetable | keep track of all my classes |
| v1.0 | student | delete modules from my timetable | remove classes I’ve dropped |
| v1.0 | student | list all my modules | see my complete schedule at a glance |
| v1.0 | student | check for timetable clashes | avoid scheduling conflicts |
| v2.0 | student | filter modules by day | see what classes I have on specific days |
| v2.0 | student | filter modules by session type | quickly find all my tutorials or labs |
| v2.0 | student | search modules by code or name | locate specific modules without scanning the entire list |
| v2.0 | student | add my grades for completed courses | maintain an academic record |
| v2.0 | student | add predicted grades for courses | manage expectations for courses |
| v2.0 | student | calculate my GPA automatically | track my academic performance |
| v2.0 | student | calculate my Major GPA automatically | track my academic performance with clearer picture |
| v2.0 | student | calculate my projected GPA | manage and set strategy for studying |
| v2.0 | student | store score breakdowns for modules | track individual assessment components |
| v2.0 | student | add reviews for courses | share my experiences with other students |
| v2.0 | student | read reviews for courses | make informed decisions about module selection |
| v2.0 | student | reset my timetable | start fresh for a new semester |
| v2.0 | student | rate a course | share simple feedback on module quality |
| v2.0 | student | search for reviews by user | see all feedback from a specific person |
| v2.0 | student | search for reviews by user & course | find a specific person’s review for a course |
| v2.0 | (dev) | manually reload reviews from file | test persistence without restarting the app |
| v2.0 | (dev) | reset all reviews | clear the in-memory state for testing (without affecting the file) |
| v2.1 | (student) | view all scores for a module | review performance breakdowns across assessments |
| v2.1 | (student) | update component scores for a module | correct mistakes or refine ongoing assessments |
| v2.1 | (student) | view the average rating for a module | decide which modules to take next semester |
| v2.1 | student | be prompted to save reviews on exit | not lose work if user forget to save manually |
| v2.1 | student | count reviews for a course | see how many reviews a course has |
| v2.1 | student | show/remove grades of the record | have more organised record and better management of academic progress |
| v2.1 | (dev) | manually merge reviews to disk | save user progress without exiting or reloading |
Usability: Commands should be intuitive and follow consistent patterns. Error messages must be clear and guide users toward correct usage.
Performance: The application should respond to commands instantly (< 100ms) for typical operations with up to 50 modules.
Reliability: Data persistence for reviews is user-controlled. The ExitSaveManager prompts the user to save unsaved changes on graceful exit (bye) to prevent data loss.
Portability: The application should run on any system with Java 11 or higher installed (Windows, macOS, Linux).
Maintainability: Code should follow object-oriented principles with clear separation of concerns. Each command class should be independent and easily modifiable.
Data Integrity: Grade point calculations must be accurate. Invalid grades should be rejected. For review data, the ReviewCleaner component ensures corrupted or incomplete entries are automatically removed from the data file to prevent loading invalid data.
Scalability: The filtering mechanism should handle multiple filter criteria efficiently using predicate-based filtering.
java -jar Uniflow.jarAdding modules:
insert i/CS2113 n/Software Engineering d/Monday f/14:00 t/16:00 s/lecture
insert i/CS2113 n/Software Engineering d/Tuesday f/10:00 t/11:00 s/tutorial
insert i/MA1521 n/Calculus d/Wednesday f/09:00 t/11:00 s/lecture
Expected: Each module is added with confirmation message showing module count.
Testing clash detection:
insert i/ST2334 n/Statistics d/Monday f/15:00 t/17:00 s/lecture
Expected: Warning about clash with CS2113 on Monday. Application prompts for confirmation.
Listing modules:
list
Expected: All added modules displayed with their details.
Deleting modules:
delete index/1
Expected: Module at index 1 of the timetable removed. Confirmation message shows remaining module count.
Filter by day:
filter day/Monday
Expected: Shows only modules scheduled on Monday.
Filter by session type:
filter type/tutorial
Expected: Shows only tutorial sessions.
Filter modules with tutorials:
filter hastutorial
Expected: Shows modules that have tutorial sessions.
Filter by module code:
filter id/CS
Expected: Shows all modules with “CS” in their code.
Adding grades:
addgrade c/CS2113 cr/4 g/A m/true
addgrade c/MA1521 cr/4 g/B+ m/true
addgrade c/ST2334 cr/4 g/A- m/true
Expected: Each course is added to academic record with confirmation.
Computing GPA:
gpa
Expected: Displays total courses studied, total grade points, total credits studied, and calculated GPA (for both all courses and major required GPA).
Testing invalid grade:
addgrade c/CS1010 cr/4 g/Z m/true
Expected: Error message about invalid grade.
Testing invalid IsMajor:
addgrade c/CS1010 cr/4 g/Z m/test
Expected: Error message that reminds users to input true/false.
Testing missing components:
addgrade c/CS1010 g/Z m/test
Expected: Error message that reminds users to follow the correct format.
Testing invalid index for removegrade:
removegrade -1
Expected: Error message that reminds users to enter a valid index.
Testing Storage:
addgrade.showgrade.Adding score breakdown:
score CS2113 exam:50 project:30 participation:20
Expected: Confirmation that breakdown is saved for CS2113.
Viewing score breakdown:
score CS2113
Expected: Show the score breakdown saved for CS2113.
Testing invalid breakdown format:
score CS2113 exam:fifty
Expected: Error message about invalid format.
Adding reviews (in-memory):
addreview c/CS2113 u/Alice r/Great course, very practical!
addreview c/CS2113 u/Bob r/Challenging but rewarding
Expected: Confirmation that reviews are added (to memory).
Viewing reviews (from memory):
review CS2113
Expected: Both reviews displayed with usernames.
Viewing non-existent course:
review CS9999
Expected: Message indicating no reviews found.
Testing Find Review:
findreview u/Alice
Expected: Shows only the review from Alice (Great course, very practical!).
findreview u/Bob c/CS2113
Expected: Shows only Bob’s review for CS2113.
findreview u/NonExistentUser
Expected: Message indicating no reviews found for this user.
Testing Count Reviews:
amount reviews c/CS2113
Expected: Message showing the count of reviews for CS2113 (e.g., “Course CS2113 has 2 review(s).”).
Test 1: Resetting memory First, add a new review to memory:
addreview c/TEST101 u/Tester r/Hello
Expected: Confirmation that review is added.
reset all reviews
Expected: All reviews have been cleared from memory.
review TEST101
Expected: No reviews found.
Test 2: Reloading from file, run the load command
load reviews
Expected: All reviews successfully reloaded from file.
review CS2113
Expected: Shows default reviews from file.
Adding a rating
rate CS2113 4
Expected: Message confirming that the rating has been added. Viewing Average Rating
rate CS2113
Expected: Displays the average rating and number of ratings per course.
Show timetable:
show timetable
Expected: All modules displayed with index numbers.
Reset timetable:
reset timetable
Expected: Confirmation that timetable is cleared. Subsequent list should show empty timetable.
Empty command:
(press Enter without typing anything)
Expected: Error message about empty command.
Invalid command:
xyz
Expected: Error message about invalid command.
Missing parameters:
insert i/CS2113
Expected: Error about missing fields.
addreview commandsbyereview command to verify reviews are loadedCheck that data/reviews.txt file exists and contains review data in format: course |
user | review |