PROJECT: RecruitBook


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 the MIN_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:

BlacklistTag

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 inside internalList using Candidate#isSameCandidate to define equality.

  • UniqueCandidateList#setCandidate(Candidate target, Candidate editedCandidate) — Replaces the Candidate object(target) in the internalList to the newly edited Candidate object(editedCandidate) through List#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 inside internalList using Company#isSameCompany to define equality.

  • UniqueCompanyList#setCompany(Company target, Company editedCompany) — Replaces the Company object(target) in the internalList to the newly edited Company object(editedCompany) through List#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 inside internalList using JobOffer#isSameJobOffer to define equality.

  • UniqueJobList#setJobOffer(JobOffer target, JobOffer editedJobOffer) — Replaces the Job Offer object(target) in the internalList to the newly edited Job Offer object(editedJobOffer) through List#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.

  1. UniqueCandidateList will be initialised with the list of saved candidates in RecruitBook

  2. UniqueCompanyList will be initialised with the list of saved companies in RecruitBook

  3. 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 …​

  1. editc 1 n/John Doe to edit the name of the 1st person in the RecruitBook.

    1. The name attribute in EditPersonDescriptor in EditCandidateCommand 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".

    2. EditCandidateCommand then calls Model#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.

  2. editC 1 c/KFC to edit the name of the 1st company in the RecruitBook.

    1. The name attribute in EditCompanyDescriptor in EditCompanyCommand 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".

    2. EditCompanyCommand then calls Model#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.

  3. editj 1 j/Cashier to edit the job title of the 1st job offer in the RecruitBook.

    1. The job title attribute in EditJobOfferDescriptor in EditJobDetailsCommand 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".

    2. EditJobDetailsCommand then calls Model#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
EditSequenceDiagram

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 the internalList of Candidates by their names.

  • UniqueCandidateList#sortByAge() — Sorts the internalList of Candidates by their age.

  • UniqueCandidateList#sortByEmail() — Sorts the internalList of Candidates by their emails.

  • UniqueCandidateList#sortByJob() — Sorts the internalList of Candidates by their job titles.

  • UniqueCandidateList#sortByEducation() — Sorts the internalList of Candidates by their education levels.

  • UniqueCandidateList#sortBySalary() — Sorts the internalList of Candidates by their salaries.

  • UniqueCandidateList#sortInReverse() — Sorts the internalList 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 the internalList of Companies by their names.

  • UniqueCompanyList#sortByEmail() — Sorts the internalList of Companies by their emails.

  • UniqueCompanyList#sortInReverse() — Sorts the internalList 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 the internalList of Job Offers by the company names they are attached to.

  • UniqueCompanyJobList#sortByJob() — Sorts the internalList of Job Offers by their job titles.

  • UniqueCompanyJobList#sortByAgeRange() — Sorts the internalList of Job Offers by the minimum age of their required age ranges.

  • UniqueCompanyJobList#sortByEducation() — Sorts the internalList of Job Offers by their required education levels.

  • UniqueCompanyJobList#sortBySalary() — Sorts the internalList of Job Offers by their salaries.

  • UniqueCompanyJobList#sortInReverse() — Sorts the internalList 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.

  1. UniqueCandidateList will be initialised with the list of saved candidates in CandidateBook.

  2. UniqueCompanyList will be initialised with the list of saved companies in CompanyBook.

  3. UniqueCompanyJobList will be initialised with the list of saved job offers in CompanyBook.

Assume that upon initialisation, there are candidate, company and job entries in RecruitBook.

Step 2. The user executes…​

  1. sortc s/ to sort the list of candidates based on their salaries.

    1. s/ prefix is used which calls the UniqueCandidateList#sortBySalary() from CandidateBook.

    2. The internalList within CandidateBook is then sorted numerically based on salary and the newly sorted list will be reflected in the MainWindow of RecruitBook.

  2. sortC c/ to sort the list of companies based on their names.

    1. c/ prefix is used which calls the UniqueCompanyList#sortByCompanyName() from CompanyBook.

    2. The internalList within CompanyBook is then sorted lexicographically based on their names and the newly sorted list will be reflected in the MainWindow of RecruitBook.

  3. sortj j/ to sort the list of job offers based on their title.

    1. j/ prefix is used which calls the UniqueCompanyJobList#sortByJob() from CompanyBook.

    2. The internalList within UniqueCompanyJobList is then sorted lexicographically based on their titles and the newly sorted list will be reflected in the MainWindow 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

  1. 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.
SortSequenceDiagram

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).
SortActivityDiagram

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 the internalList 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.

  1. 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.

  1. 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.

  1. blacklist rm 1 is executed. The 1st candidate will no longer be blacklisted and the "BLACKLISTED" tag will be removed from its entry.

  2. 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:

BlacklistSequenceDiagram

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).