1. Overview
This portfolio page documents my involvement in SocialCare, a project done as part of the CS21303T Software Engineering module taught in NUS. For this project, my team had the goal to 'harness the potential of software to create sustainable social change'.
With our goal in mind, we developed SocialCare, a desktop application made for social welfare organizations. This application is designed to help manage volunteers and events effectively. This is done through the Command Line Interface (CLI), which help users to interact with the application; and the Graphical User Interface (GUI), which displays information in a user-friendly manner.
My main role as a developer for SocialCare was to implement record management functions, which enable users to do the following:
-
View volunteers assigned to an event
-
Assign volunteers to an event
-
Update volunteering records for volunteers
-
Delete volunteering records of volunteers
2. Summary of Contributions
-
Major enhancement: Added commands to manage volunteer records
-
What it does: The commands implemented allow users to manage volunteering records. They will be able to assign volunteers to events and update the number of hours contributed by the volunteer.
-
Justification: These functions are part of the core requirements for the application because we want users to be able to effectively manage volunteering records.
-
Highlights: This enhancement is completely new and required an in-depth analysis of design alternatives. It was challenging as to properly implement this, a complete understanding of all code components was needed.
-
-
Minor enhancements:
-
Added a command to allow users to switch between panels.
-
Created charts for the
overview
command.
-
-
Code contributed: Here is a link to my code on the Project Code Dashboard.
-
Other contributions:
-
Project management:
-
Set up the team repository on GitHub
-
Ensured that project deliverables were done on time and in the right format
-
-
Documentation:
-
Community:
-
Tools:
-
Integrated Travis to the Github repository
-
-
3. Contributions to the User Guide
Below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ |
3.1. Viewing event’s volunteer records: manage
The panel updates to show the list of volunteers while the display area shows the volunteers currently registered for the selected event.
Format: manage EVENT_INDEX
Example(s):
-
switch -e
(panel updates to show list of events)
manage 1
(view the list of volunteers assigned to the event at index 1)
See the figures below for a step-by-step guide.
switch -e
manage 1
targets the event at index 1manage 1
Refer to the labelled sections in the figure above for the following changes:
-
The panel displays the list of volunteers that you can assign to an event.
-
Name of the event being managed and total number of volunteers assigned to it.
-
The list of volunteers currently assigned to the event. It will be empty if there are no volunteers assigned.
3.2. Switching panels: switch
Switches the panel to display either volunteers or events
Format: switch -CONTEXT_ID
Example(s):
-
switch -e
Updates the panel displaying the list of volunteers to display the list of events.
The following figure shows the expected panel before and after entering theswitch
command.
4. Contributions to the Developer Guide
Below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ |
4.1. Manage command
The manage
command is used in the 'event' context to manage the volunteering records for an event.
Current implementation
This manage
command requires the ManageCommandParser
class to parse user input and determine which event to manage.
ManageCommandParser
implements the Parser
class which has the Parser#parse()
operation. This operation will throw an error if the user input is an invalid event id.
The manage
command updates the context found in ModelManager
through the model#switchToRecordContext()
function.
In addition to updating the context, the manage
command also does the following:
-
Clears all predicates for volunteer list.
-
Filters the existing records by the selected event.
-
Resets the state pointer (for undo/redo functions).
-
Raises
RecordChangeEvent
to set the selected event. -
Raises
ContextChangeEvent
to update the UI.
The following code snippet shows what the manage
command does upon execution:
public class ManageCommand extends Command {
//...
@Override
public CommandResult execute(Model model, CommandHistory history) throws CommandException {
requireNonNull(model);
List<Event> filteredEventList = model.getFilteredEventList();
model.updateFilteredVolunteerList(PREDICATE_SHOW_ALL_VOLUNTEERS);
if (targetIndex.getZeroBased() >= filteredEventList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_EVENT_DISPLAYED_INDEX);
}
model.switchToRecordContext();
model.setSelectedEvent(filteredEventList.get(targetIndex.getZeroBased()));
model.updateFilteredRecordList(new RecordContainsEventIdPredicate(
filteredEventList.get(targetIndex.getZeroBased()).getEventId()
));
model.resetStatePointer();
EventsCenter.getInstance().post(new RecordChangeEvent(
filteredEventList.get(targetIndex.getZeroBased())));
EventsCenter.getInstance().post(new ContextChangeEvent(model.getContextId()));
return new CommandResult(String.format(MESSAGE_MANAGE_EVENT_SUCCESS,
filteredEventList.get(targetIndex.getZeroBased()).getName().fullName)
+ " [" + targetIndex.getOneBased() + "]");
}
//...
}
From the code snippet above, we see that the current state of event list from the model is stored into another list called 'filteredEventList'. Storing the events in another list is done so that the list can be easily referenced in later parts of the code.
The volunteer list in the model is updated with the predicate so that it now contains the list of all volunteers in the system.
A quick check is done to ensure that the user input is valid. Otherwise, an exception is thrown.
If the user input is valid, the application changes to the record context. Then, the selected event by the user is stored in the model.
In addition, the model resets the state pointer so that the undo
and redo
functions will point to a fresh, new state.
Lastly, all the relevant UI is updated by posting events via the EventsCenter
.
The figure below is the sequence diagram to show how the switch
command works when switching from volunteer to event context.
Design considerations
Aspect: Context switching (to volunteering records)
-
Alternative 1 (current choice): Utilize
Context
class used in theswitch
function. (See Switch command)Pros
No need to create a new class to change context.
Cons
Have to create a new method in
Context
class to handle parsed user input. -
Alternative 2: Pass event and volunteer objects via
LogicManager
.Pros
Implementation is easy.
Cons
Classes becomes tightly coupled. The UI component would have access to methods it does not need.
4.2. Switch command
The switch
command is used to switch the context between 'volunteer' and 'event'.
Current implementation
This switch
command requires the SwitchCommandParser
class to parse user input and help determine the context to switch to.
SwitchCommandParser
implements the Parser
class which has the Parser#parse()
operation. This operation will throw an error if the user input does not match the command format or is an invalid context to switch to.
There are only 2 valid contexts which a user can switch to with the command. v: 'volunteer' context e: 'event' context |
The switch
command updates the context found in ModelManager
before raising the context change event to update the UI.
In addition to updating the context, the switch
command also does the following:
-
Clears all predicates for volunteers, events and record lists.
-
Resets the state pointer (for undo/redo functions).
-
Raises
ContextChangeEvent
to update the UI.
The following code snippet shows what the switch
command does upon execution:
public class SwitchCommand extends Command {
//...
public SwitchCommand(String contextToSwitch) {
requireNonNull(contextToSwitch);
contextId = contextToSwitch;
}
@Override
public CommandResult execute(Model model, CommandHistory history) {
requireNonNull(model);
model.setCurrentContext(contextId);
model.updateFilteredVolunteerList(Model.PREDICATE_SHOW_ALL_VOLUNTEERS);
model.updateFilteredEventList(Model.PREDICATE_SHOW_ALL_EVENTS);
model.updateFilteredRecordList(Model.PREDICATE_SHOW_ALL_RECORDS);
model.resetStatePointer();
EventsCenter.getInstance().post(new ContextChangeEvent(contextId));
return new CommandResult(String.format(MESSAGE_SUCCESS, model.getContextName()));
}
}
From the code snippet above, we see that upon calling the SwitchCommand
, a contextId is set.
The contextId (which is retrieved from the user’s input) is either 'e' for event or 'v' for volunteers.
This contextId will be used when the execute method is called.
When the execute method is called, the context is set in the model via the 'setCurrentContext' method. The model contains different methods to update the various lists. A predicate is passed to reset each of the lists to the initial state.
The model also resets the state pointer so that the undo
and redo
functions will point to a fresh, new state.
Lastly, the EventsCenter
posts a new event so that the panels would update accordingly and display the relevant lists.
The figure below is the sequence diagram to show how the switch
command works when switching from volunteer to event context.
Design considerations
Aspect: How context is maintained
-
Alternative 1 (current choice): Create a new
Context
class.Pros
Can support even more contexts in the future due to the flexibility of a class.
Cons
Tedious to do as relevant methods have to be implemented in model.
-
Alternative 2: Pass a hard-coded context id around.
Pros
No need to create a new object to handle the context.
Cons
Difficult to maintain the id throughout the whole application. Any change in context id would require all the codes to be updated.