Overview
This project portfolio page serves to document my contributions to the project "RecruitBook". The project was created for the "CS2113T: Software Engineering & Object-Oriented Programming" module that I took during my second year of study in the National University Of Singapore(NUS). RecruitBook is referenced from AddressBook 4 created by the SE-EDU team from NUS and it is then further developed by my team.
My team consists of 5 members who majored in the field of Computing and we each handled different aspects of RecruitBook ranging from its Graphical User Interface(GUI) to its core functionalities. My role as a developer in this project was to manage the modifying and handling of data. I had to ensure that different components are properly integrated in such a way that any data modifications would be propagated throughout RecruitBook. This ensures that the data present in RecruitBook is always consistent.
RecruitBook is a desktop application created for job agents. It serves as a management tool to better manage both a list of job applicants and job offers. The user interacts with it using a Command Line Interface, and it has a GUI created with JavaFX. It is written in Java, and has about 10k lines of code.
Currently, these are the features available in RecruitBook:
-
Adding and deleting of data, modifying of data, fetching of data, shortlisting, emailing and blacklisting
Summary of contributions
-
Major enhancement: added the ability to edit entries
-
What it does:
It allows the user to edit details of candidates, companies and job offers -
Justification:
This feature gives users the freedom to update the saved data in RecruitBook, making it easier to manage the different lists as over time, entries might require changes. Any unwanted typing mistakes or misinformation can be easily rectified as well. -
Credits: AB4
-
-
Major enhancement: added the ability to sort entries
-
What it does:
It allows users to sort the various lists (candidate, company or job offers) by different attributes such as their names. -
Justification:
This feature enables users to control how they want their lists listed. It makes it easier to determine how different entries compare to one another by sorting them based on selected attributes. -
Highlights:
Apart from the attributes, users can also do a reverse sort to list out the current list in reverse order (both increasing and decreasing order).
-
-
Minor enhancement:
-
Added a Blacklist Command that blacklists undesired candidates. Serves like a blocking feature that prevents users from carelessly shortlisting or editing these candidates. Blacklisted candidated are tagged with "BLACKLISTED".
-
-
Code contributed: [Functional/Test code]
-
Other contributions:
-
Project management:
-
Managed releases (by milestones)
v1.1
-v1.3.1
(3 releases) on GitHub
-
-
Enhancements to existing features:
-
Added a list of enums for the education level attribute in RecruitBook. Makes sorting by education level easier.
-
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Editing a candidate / company / job : editc
editC
editj
Edits attributes of the candidate at the specified index INDEX
Format: editc <INDEX>
You can edit candidates with their specified INDEX
.
The index must be an index number shown in the displayed candidates list.
Example:
-
editc 1 n/John Doe p/91234567
(Edits the name, phone number and email address of the candidate with index 1 to be John Doe and 91234567 respectively) -
editc 3 t/
(Edits and clears all existing tags belonging to the candidate at index 3)
Edits details of a company at the specified index INDEX
Format: editC <INDEX>
You can edit companies with their specified INDEX
.
The index must be an index number shown in the displayed company list.
Example:
-
editC 1 c/KFC p/62226111
(Edits the name and phone number of the company with index 1 to be KFC and 62226111
Edits details of a job offer
Format: editj <INDEX>
You can edit job offers with their specified INDEX
.
This index must be an index number shown in the displayed job list.
You need to enter listC first to see the full list of job offers! The <INDEX> is based on listC !
|
Examples:
-
listC
-
editj 1 xr/20-30
(Edits theMIN_AGE-MAX_AGE
attribute of the 1st job offer to be of 20 to 30 years of age)
Sorting candidates / company / job : sortc
sortC
sortj
Sorting candidates based on different fields
Format: sortc <tag>
Supported tags: n/
, x/
, e/
, j/
, h/
, s/
, r/
-
Only one tag/field is included when using the sort command
-
You must have one tag/field included
Examples:
-
sortc n/
(Sorts all candidates lexicographically by their name) -
sortc r/
(Sorts all candidates in reverse)
Sorting companies based on different fields
Format: sortC <tag>
Supported tags: c/
, e/
, r/
-
Only one tag/field is included when using the sort command
-
You must have one tag/field included
Examples:
-
sortC c/
(Sorts all companies lexicographically by their company’s name) -
sortC r/
(Sort all companies in reverse)
Sorting job offers based on different fields
Format: sortj <tag>
Supported tags: c/
, j/
, xr/
, h/
, s/
, r/
-
Only one tag/field is included when using the sort command
-
You must have one tag/field included
You should enter listC first to see the full list of job offers for sorting!
|
Examples:
-
listC
-
sortj j/
(Sorts all job offers lexicographically by their titles) -
sortj r/
(Sorts all job offers in reverse)
Blacklisting candidates : blacklist
blacklist rm
Format: blacklist
<INDEX>
You can use this command to blacklist candidates with their specified INDEX
.
The index must be an index number shown in the displayed candidates list
The candidates you blacklisted can no longer be edited or shortlisted.
Candidates can be unblacklisted by using blacklist rm .
|
Blacklisted Candidates have this tag:
Examples:
-
blacklist 1
(Blacklists first candidate in the list) -
blacklist rm 1
(Unblacklists first candidate in the list)
Contributions to the Developer Guide
Given 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. |
Edit Feature
Current Implementation
Candidates
The edit mechanism for candidates is facilitated by EditPersonDescriptor
in EditCandidateCommand
which creates a new Candidate object with edited attributes
and UniqueCandidateList
which stores candidates internally as an internalList
which is a list of Candidate objects.
Additionally, it implements the following operations to support the editing of candidates.
-
UniqueCandidateList#contains(Candidate candidate)
— Checks whether a candidate exists insideinternalList
usingCandidate#isSameCandidate
to define equality. -
UniqueCandidateList#setCandidate(Candidate target, Candidate editedCandidate)
— Replaces the Candidate object(target) in theinternalList
to the newly edited Candidate object(editedCandidate) throughList#set(int index, E element)
.
These operations are exposed in the Model
interface as Model#hasCandidate(Candidate candidate)
and Model#updateCandidate(Candidate target, Candidate editedCandidate)
respectively.
Companies
The edit mechanism for companies is facilitated by EditCompanyDescriptor
in EditCompanyCommand
which creates a new Company object with edited attributes
and UniqueCompanyList
which stores companies internally as an internalList
which is a list of Company objects.
Additionally, it implements the following operations to support the editing of companies.
-
UniqueCompanyList#contains(Company company)
— Checks whether a company exists insideinternalList
usingCompany#isSameCompany
to define equality. -
UniqueCompanyList#setCompany(Company target, Company editedCompany)
— Replaces the Company object(target) in theinternalList
to the newly edited Company object(editedCompany) throughList#set(int index, E element)
.
These operations are exposed in the Model
interface as Model#hasCompany(Company company)
and Model#updateCompany(Company target, Company editedCompany)
respectively.
Job Offers
The edit mechanism for job offers is facilitated by EditJobOfferDescriptor
in EditJobDetailsCommand
which creates a new Job Offer object with edited attributes
and UniqueJobList
which stores job offers internally as an internalList
which is a list of Job Offer objects.
Additionally, it implements the following operations to support the editing of job offers.
-
UniqueJobList#contains(JobOffer jobOffer)
— Checks whether a job offer exists insideinternalList
usingJobOffer#isSameJobOffer
to define equality. -
UniqueJobList#setJobOffer(JobOffer target, JobOffer editedJobOffer)
— Replaces the Job Offer object(target) in theinternalList
to the newly edited Job Offer object(editedJobOffer) throughList#set(int index, E element)
.
These operations are exposed in the Model
interface as Model#hasJobOffer(JobOffer jobOffer)
and Model#updateJobOffer(JobOffer target, JobOffer editedJobOffer)
respectively.
If you try to edit the job offer to have a company name that does not exist in the book, it will trigger a command exception! |
The editing of all three entities (Candidate, Company, JobOffer) follow the same mechanism. |
Given below is an example usage scenario and how the edit mechanism behaves at each step.
Step 1. The user launches RecruitBook for the first time.
-
UniqueCandidateList
will be initialised with the list of saved candidates in RecruitBook -
UniqueCompanyList
will be initialised with the list of saved companies in RecruitBook -
UniqueJobList
will be initialised with the list of saved job offers.Assume that upon initialisation, there are candidate, company and job offer entries in RecruitBook.
Step 2. The user executes …
-
editc 1 n/John Doe
to edit the name of the 1st person in the RecruitBook.-
The name attribute in
EditPersonDescriptor
inEditCandidateCommand
has been set to "John Doe" and the descriptor is used to compare with the original Candidate object and a new Candidate object is created with the name being "John Doe". -
EditCandidateCommand
then callsModel#hasCandidate(Candidate candidate)
to check if the edited candidate has a duplicate in the RecruitBook. If not,Model#updateCandidate(Candidate target, Candidate editedCandidate)
is then called that replaces the original Candidate object with the newly edited one.
-
-
editC 1 c/KFC
to edit the name of the 1st company in the RecruitBook.-
The name attribute in
EditCompanyDescriptor
inEditCompanyCommand
has been set to "KFC" and the descriptor is used to compare with the original Company object and a new Company object is created with the name being "KFC". -
EditCompanyCommand
then callsModel#hasCompany(Company company)
to check if the edited company has a duplicate in the RecruitBook. If not,Model#updateCompany(Company target, Company editedCompany)
is then called that replaces the original Company object with the newly edited one.
-
-
editj 1 j/Cashier
to edit the job title of the 1st job offer in the RecruitBook.-
The job title attribute in
EditJobOfferDescriptor
inEditJobDetailsCommand
has been set to "Cashier" and the descriptor is used to compare with the original JobOffer object and a new JobOffer object is created with the job title being "Cashier". -
EditJobDetailsCommand
then callsModel#hasJobOffer(JobOffer jobOffer)
to check if the edited job offer has a duplicate in the RecruitBook. If not,Model#updateJobOffer(JobOffer target, JobOffer editedJobOffer)
is then called that replaces the original JobOffer object with the newly edited one.Any duplicates formed after editing will not replace the original object. It will prompt an error message
-
The following sequence diagram shows how the edit operation works for editc:
All three variations of edit follows the same sequence during operation
|
Design Considerations
How edit executes
-
Alternative 1 (current choice): Replaces the original object with an edited one.
-
Pros: If the edited object were to result in errors, it can be prevented from replacing the original object.
-
Cons: It is less efficient as compared to editing the original object straightaway.
-
-
Alternative 2: Editing the original object instead of replacing it with an edited one.
-
Pros: It is more intuitive and efficient.
-
Cons: The edited object cannot be checked for duplicates without permanently changing the object first.
-
Data structure to support the edit command
-
Alternative 1 (current choice): Separating the editing feature into 3 commands for different entities.
-
Pros: Greater distinction between the entities.
-
Cons: More commands to deal with (similar commands across all three entities).
-
-
Alternative 2: Have a central
EditManager
that handles all edits.-
Pros: Reduction of command objects (less confusing).
-
Cons: There is coupling between the manager and the individual commands. Changes to an entity has to be cascaded to the manager.
-
Sort Feature
Current Implementation
Candidates
The sort mechanism of candidates is facilitated in UniqueCandidateList
which stores candidates in an internalList
. It implements the sorting of candidates using the following operations:
-
UniqueCandidateList#sortByName()
— Sorts theinternalList
of Candidates by their names. -
UniqueCandidateList#sortByAge()
— Sorts theinternalList
of Candidates by their age. -
UniqueCandidateList#sortByEmail()
— Sorts theinternalList
of Candidates by their emails. -
UniqueCandidateList#sortByJob()
— Sorts theinternalList
of Candidates by their job titles. -
UniqueCandidateList#sortByEducation()
— Sorts theinternalList
of Candidates by their education levels. -
UniqueCandidateList#sortBySalary()
— Sorts theinternalList
of Candidates by their salaries. -
UniqueCandidateList#sortInReverse()
— Sorts theinternalList
of Candidates in reverse order of their current order.
These operations are accessed through the Model
interface as Model#sortCandidates(Prefix prefix)
which then calls the respective sorting method by determining the prefix type in CandidateBook
.
These prefixes n/ , x/ , e/ , j/ , h/ , s/ , r/ are used respectively to the methods shown above.
|
Companies
The sort mechanism of companies is facilitated in UniqueCompanyList
which stores companies in an internalList
. It implements the sorting of companies using the following operations:
-
UniqueCompanyList#sortByCompanyName()
— Sorts theinternalList
of Companies by their names. -
UniqueCompanyList#sortByEmail()
— Sorts theinternalList
of Companies by their emails. -
UniqueCompanyList#sortInReverse()
— Sorts theinternalList
of Companies in reverse order of their current order.
These operations are accessed through the Model
interface as Model#sortCompanies(Prefix prefix)
which then calls the respective sorting method by determining the prefix type in CompanyBook
.
These prefixes c/ , e/ , r/ are used respectively to the methods shown above.
|
Job Offers
The sort mechanism of job offers is facilitated in UniqueCompanyJobList
which stores job offers in an internalList
. It implements the sorting of job offers using the following operations:
-
UniqueCompanyJobList#sortByCompanyName()
— Sorts theinternalList
of Job Offers by the company names they are attached to. -
UniqueCompanyJobList#sortByJob()
— Sorts theinternalList
of Job Offers by their job titles. -
UniqueCompanyJobList#sortByAgeRange()
— Sorts theinternalList
of Job Offers by the minimum age of their required age ranges. -
UniqueCompanyJobList#sortByEducation()
— Sorts theinternalList
of Job Offers by their required education levels. -
UniqueCompanyJobList#sortBySalary()
— Sorts theinternalList
of Job Offers by their salaries. -
UniqueCompanyJobList#sortInReverse()
— Sorts theinternalList
of Job Offers in reverse order of their current order.
These operations are accessed through the Model
interface as Model#sortJobOffers(Prefix prefix)
which then calls the respective sorting method by determining the prefix type in CompanyBook
.
These prefixes c/ , j/ , xr/ , h/ , s/ , r/ are used respectively to the methods shown above.
|
Given below is an example usage scenario and how the sort command behaves at each step.
Step 1. The user launches RecruitBook for the first time.
-
UniqueCandidateList
will be initialised with the list of saved candidates inCandidateBook
. -
UniqueCompanyList
will be initialised with the list of saved companies inCompanyBook
. -
UniqueCompanyJobList
will be initialised with the list of saved job offers inCompanyBook
.
Assume that upon initialisation, there are candidate, company and job entries in RecruitBook. |
Step 2. The user executes…
-
sortc s/
to sort the list of candidates based on their salaries.-
s/
prefix is used which calls theUniqueCandidateList#sortBySalary()
fromCandidateBook
. -
The
internalList
withinCandidateBook
is then sorted numerically based on salary and the newly sorted list will be reflected in theMainWindow
of RecruitBook.
-
-
sortC c/
to sort the list of companies based on their names.-
c/
prefix is used which calls theUniqueCompanyList#sortByCompanyName()
fromCompanyBook
. -
The
internalList
withinCompanyBook
is then sorted lexicographically based on their names and the newly sorted list will be reflected in theMainWindow
of RecruitBook.
-
-
sortj j/
to sort the list of job offers based on their title.-
j/
prefix is used which calls theUniqueCompanyJobList#sortByJob()
fromCompanyBook
. -
The
internalList
withinUniqueCompanyJobList
is then sorted lexicographically based on their titles and the newly sorted list will be reflected in theMainWindow
of RecruitBook.
-
Step 3. The user does not want the candidates to be sorted in increasing order of their salaries but decreasing order. So, the user executes
-
sortc r/
to sort the list of candidates in the reverse order of the current order.
The following sequence diagram shows how the sort operation works:
All three variations of the sort command follows the same sequence during operation.
|
The following activity diagram summarizes what happens when a user executes sortc
command:
All three variations of the sort command follows a similar activity flow, with different cases for switch(prefix).
|
Design Considerations
Aspect: How sort executes
-
Alternative 1(current choice): Store the input prefix as a parameter to be passed in to the respective books which calls the respective method.
-
Pros: The variations of sort methods are stored under
UniqueCandidateList
instead of having more command objects. -
Cons: Implementation of switch case statements in both books which could lead to poorer performance.
-
-
Alternative 2: Create multiple sort commands for the various attributes.
-
Pros: The implementation would give a better performance.
-
Cons: There will be about 10 more newly added command objects which makes it confusing when dealing with many similar commands.
-
Blacklist Feature
Current Implementation
The blacklist mechanism is facilitated by UniqueCandidateList
that is stored in the CandidateBook (as internalList
). This feature is also supported by Model#getFilteredCandidateList
where the input index is used to obtain the Candidate object from the observable list.
Additionally, it implements the following operations:
-
UniqueCandidateList#setCandidate(Candidate target, Candidate editedCandidate)
— Replaces the original Candidate object in theinternalList
with the newly edited Candidate object.
This operation is exposed in the Model
interface as Model#updateCandidate(Candidate target, Candidate editedCandidate)
.
There is no duplicate check unlike edit as blacklist only involve tags and they are not attributes that determine the uniqueness of candidates. |
Given below is an example usage scenario and how the blacklist command behaves at each step.
Step 1. The user launches RecruitBook for the first time.
-
UniqueCandidateList
is initialised with the saved data of Candidates in RecuitBook.
Assume that upon initialisation, there are candidate entries in RecruitBook. |
Step 2. The user executes blacklist 1
command to blacklist the 1st candidate in the candidate book. If there are no errors, the 1st candidate will be blacklisted and a "BLACKLISTED" tag will appear in its entry.
-
It achieves this by generating a new Candidate object with a blacklist tag then replaces the original Candidate object in the
internalList
.
Step 3. The user executes editc 1 n/James
. This command will fail to execute as blacklisted candidates cannot be edited or shortlisted. An error message will appear.
Step 4. The user decides that blacklisting the candidate was a mistake and proceeds to remove the blacklist.
-
blacklist rm 1
is executed. The 1st candidate will no longer be blacklisted and the "BLACKLISTED" tag will be removed from its entry. -
It achieves this by generating a new Candidate object without a blacklist tag which replaces the original blacklisted Candidate in the
internalList
.
Trying to blacklist an already blacklisted candidate will prompt an error message. Unblacklisting a candidate who is not blacklisted will do likewise. |
The following sequence diagram shows how the blacklist operation works:
Design considerations
Aspect: How blacklist executes
-
Alternative 1(current choice): Creates a new Candidate object for adding or removal of blacklist.
-
Pros: Less error prone as compared to editing candidates straight.
-
Cons: It is less efficient to regenerate objects as compared to directing editing of candidates.
-
-
Alternative 2: Editing the tags of the Candidate directly.
-
Pros: More intuitive and efficient.
-
Cons: Code becomes less defensive as any changes will be permanent reflected in that candidate.
-
Aspect: Data structure to support the blacklist command
-
Alternative 1(current choice): Add blacklist tags into the candidates.
-
Pros: Blacklisting can be immediately displayed on entries.
-
Cons: There is no organised data structure that accommodates all blacklisted candidates, so if there is a need for all blacklisted candidates, every candidate has to be checked.
-
-
Alternative 2: Store blacklisted candidates in a list.
-
Pros: There is a one-stop collection of blacklisted candidates.
-
Cons: To check if a candidate if blacklisted, a loop has to be used to check the list. (slows the execution of edit and shortlist).
-