diff --git a/.gitattributes b/.gitattributes index d72fd520b1c..3f56955acd8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,15 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary *.pdf binary diff --git a/.gitignore b/.gitignore index 5e59b862ba4..137d3218ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ src/main/resources/docs/ /.idea/ /out/ /*.iml +/src/main/resources/main.iml +/src/test/resources/test.iml # Storage/log files /data/ diff --git a/.travis.yml b/.travis.yml index 924a42eb8da..bb5835f7f69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,13 @@ matrix: include: - jdk: openjdk11 -script: >- - ./config/travis/run-checks.sh && - ./gradlew clean checkstyleMain checkstyleTest test coverage coveralls asciidoctor +services: + - xvfb +script: >- + ./config/travis/run-checks.sh && + ./gradlew clean checkstyleMain checkstyleTest headless && + ./gradlew test coverage coveralls asciidoctor deploy: skip_cleanup: true provider: script diff --git a/README.adoc b/README.adoc index e36efe534bb..6aa7fa97d07 100644 --- a/README.adoc +++ b/README.adoc @@ -1,10 +1,9 @@ -= Address Book (Level 3) += HireLah! ifdef::env-github,env-browser[:relfileprefix: docs/] -https://travis-ci.org/se-edu/addressbook-level3[image:https://travis-ci.org/se-edu/addressbook-level3.svg?branch=master[Build Status]] -https://ci.appveyor.com/project/damithc/addressbook-level3[image:https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true[Build status]] -https://coveralls.io/github/se-edu/addressbook-level3?branch=master[image:https://coveralls.io/repos/github/se-edu/addressbook-level3/badge.svg?branch=master[Coverage Status]] -https://www.codacy.com/app/damith/addressbook-level3?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level3&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a[Codacy Badge]] +https://travis-ci.com/AY1920S2-CS2103-W15-2/main[image:https://travis-ci.com/AY1920S2-CS2103-W15-2/main.svg?branch=master[Build Status]] +https://ci.appveyor.com/project/CornCobs/main[image:https://ci.appveyor.com/api/projects/status/uvqpdgd3xsbcuyn4?svg=true[Build Status]] +https://coveralls.io/github/AY1920S2-CS2103-W15-2/main[image:https://coveralls.io/repos/github/AY1920S2-CS2103-W15-2/main/badge.svg[Coverage Status]] ifdef::env-github[] @@ -15,9 +14,23 @@ ifndef::env-github[] image::images/Ui.png[width="600"] endif::[] -* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as the main programming language. -* It is *written in OOP fashion*. It provides a *reasonably well-written* code example that is *significantly bigger* (around 6 KLoC)than what students usually write in beginner-level SE modules. +*HireLah!* is a productivity app designed to help busy interviewers manage multiple interview sessions in an orderly +manner. During each interview session, *HireLah!* helps the user to keep track of interviewee information and interview +rubrics in one central location, making the interview process more efficient. + +*HireLah!* has the following main features: + +. Manage multiple interview sessions. +. Manage interview rubrics for each interview session, including: +.. Attributes to look out for in interviewees. +.. Questions to ask interviewees. +.. Metrics to rank interviewees based on their performance. +. Manage remarks and score entered by the user during an interview. +. Rank interviewees based on particular sets of attributes or a metric after interviews. +. Export interview transcripts as reference. + +*HireLah!* is a desktop application. It has a GUI (Graphical User Interface), but most of the user interactions occur +using a CLI (Command Line Interface) == Site Map @@ -29,8 +42,10 @@ endif::[] == Acknowledgements +* This application was built upon the AddressBook-Level3 project created by https://se-education.org[SE-EDU initiative]. * Some parts of this sample application were inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by _Marco Jakob_. -* Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/junit-team/junit5[JUnit5] +* Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], +https://github.com/junit-team/junit5[JUnit5], https://github.com/TestFX/TestFX[TestFX], https://pdfbox.apache.org/[Apache PDFBox] == Licence : link:LICENSE[MIT] diff --git a/SomeFile.json b/SomeFile.json new file mode 100644 index 00000000000..1849a72dc75 --- /dev/null +++ b/SomeFile.json @@ -0,0 +1,3 @@ +{ + "finalised" : null +} diff --git a/build.gradle b/build.gradle index 93029ef8262..f50fedd9cc7 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { } // Specifies the entry point of the application -mainClassName = 'seedu.address.Main' +mainClassName = 'hirelah.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -41,6 +41,8 @@ test { useJUnitPlatform() } + + dependencies { String jUnitVersion = '5.4.0' String javaFxVersion = '11' @@ -61,15 +63,25 @@ dependencies { implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: jUnitVersion + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion + + + testCompile "org.testfx:testfx-core:4.0.16-alpha" + testCompile "org.testfx:testfx-junit5:4.0.16-alpha" + testCompile group: 'org.hamcrest', name: 'hamcrest', version: '2.1' + + testRuntimeOnly group: 'org.testfx', name: 'openjfx-monocle', version: 'jdk-11+26' + + compile 'org.apache.pdfbox:pdfbox:2.0.19' } shadowJar { - archiveName = 'addressbook.jar' - - destinationDir = file("${buildDir}/jar/") + archiveBaseName = "hirelah" + archiveVersion = "v1.4" } task coverage(type: JacocoReport) { @@ -87,6 +99,23 @@ task coverage(type: JacocoReport) { } } +task headless { + doLast { + println 'Setting headless mode properties.' + test { + systemProperties = [ + 'java.awt.headless': 'true', + 'testfx.robot': 'glass', + 'testfx.headless': 'true', + 'prism.order': 'sw', + 'prism.text': 't2k', + 'headless.geometry': '1600x1200-32' + ] + } + } +} + + coveralls { sourceDirs = sourceSets.main.allSource.srcDirs.absolutePath jacocoReportPath = "${buildDir}/reports/jacoco/coverage/coverage.xml" @@ -133,8 +162,8 @@ asciidoctor { idprefix: '', // for compatibility with GitHub preview idseparator: '-', 'site-root': "${sourceDir}", // must be the same as sourceDir, do not modify - 'site-name': 'AddressBook-Level3', - 'site-githuburl': 'https://github.com/se-edu/addressbook-level3', + 'site-name': 'HireLah!', + 'site-githuburl': 'https://github.com/AY1920S2-CS2103-W15-2/main', 'site-seedu': true, // delete this line if your project is not a fork (not a SE-EDU project) ] diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc index 458e6134f45..848ace49586 100644 --- a/docs/AboutUs.adoc +++ b/docs/AboutUs.adoc @@ -4,53 +4,71 @@ :imagesDir: images :stylesDir: stylesheets -AddressBook - Level 3 was developed by the https://se-edu.github.io/docs/Team.html[se-edu] team. + -_{The dummy content given below serves as a placeholder to be used by future forks of the project.}_ + -{empty} + +HireLah! is targeted to bring greater convenience to interviewers who can type quickly and prefer typing over mouse + +commands. Additionally, we included customised features in our desktop apps to help interviewers to manage interviews. + We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore]. == Project Team -=== John Doe -image::damithc.jpg[width="150", align="left"] -{empty}[http://www.comp.nus.edu.sg/~damithch[homepage]] [https://github.com/damithc[github]] [<>] +=== Chin Synn Khee, Joash +image::corncobs.png[width="200", align="left"] +{empty}[http://github.com/CornCobs[github]] [<>] -Role: Project Advisor +Role: Team Lead + +Responsibilities: + +- Maintains the standard of the code quality + +- Designs the feature for interview phase + +- Extensively create tests for the app + +- Assists in finding a solution when other members have issues ''' -=== John Roe -image::lejolly.jpg[width="150", align="left"] -{empty}[http://github.com/lejolly[github]] [<>] +=== Mario Lorenzo +image::mario7lorenzo.png[width="200", align="left"] +{empty}[https://github.com/mario7lorenzo[github]] [<>] -Role: Team Lead + -Responsibilities: UI +Role: Developer (Logic and Model) + +Responsibilities: + +- Creates the Model as the building blocks of the app + +- Designs commands for the normal phase, and the decision phase + +- Reminds the others regarding deadlines of the project + +- Ensure the documentation consistent ''' -=== Johnny Doe -image::yijinl.jpg[width="150", align="left"] -{empty}[http://github.com/yijinl[github]] [<>] +=== Yan Boshen +image::bscrow.png[width="200", align="left"] +{empty}[http://github.com/bscrow[github]] [<>] -Role: Developer + -Responsibilities: Data +Role: Developer (UI) + +Responsibilities: + +- Reworks the UI of AB3 to fits the desired product. + +- Extensively create tests for the app + +- Contributes to the documentation of the UI ''' -=== Johnny Roe -image::m133225.jpg[width="150", align="left"] -{empty}[http://github.com/m133225[github]] [<>] +=== Agnes Natasya +image::agnesnatasya.png[width="200", align="left"] +{empty}[http://github.com/agnesnatasya[github]] [<>] -Role: Developer + -Responsibilities: Dev Ops + Threading +Role: Developer (Logic and Model) + +Responsibilities: + +- Creates the parser to convert the input of the user to a command + +- Creates tests for the normal parsers + +- Contributes to the documentation of the Logic and Model ''' -=== Benson Meier -image::yl_coder.jpg[width="150", align="left"] -{empty}[http://github.com/yl-coder[github]] [<>] +=== Ong Wei Cheng +image::Weiichengg.png[width="200", align="left"] +{empty}[http://github.com/Weiichengg[github]] [<>] -Role: Developer + -Responsibilities: UI +Role: Developer (Storage) + +Responsibilities: + +- Designs the structure of the storage + +- Reworks on the storage of AB3 + +- Creates tests for the storage part ''' diff --git a/docs/ContactUs.adoc b/docs/ContactUs.adoc index 81be279ef6d..1a255ddf16f 100644 --- a/docs/ContactUs.adoc +++ b/docs/ContactUs.adoc @@ -2,6 +2,6 @@ :site-section: ContactUs :stylesDir: stylesheets -* *Bug reports, Suggestions* : Post in our https://github.com/se-edu/addressbook-level3/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. +* *Bug reports, Suggestions* : Post in our https://github.com/AY1920S2-CS2103-W15-2/main/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. * *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here] -* *Email us* : You can also reach us at `damith [at] comp.nus.edu.sg` +* *Email us* : You can also reach us at `e0369915 [at] gmail.com` diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 3d65905a853..c0f54a8da3f 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - Developer Guide += HireLah! - Developer Guide :site-section: DeveloperGuide :toc: :toc-title: @@ -12,9 +12,35 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3/tree/master +:repoURL: https://github.com/AY1920S2-CS2103-W15-2/main -By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` +By: `AY1920S2-CS2103-W15-2`      Since: `Feb 2020`      Licence: `MIT` + +== Introduction + +*HireLah!* is a productivity app designed to help busy interviewers manage multiple interview sessions in an orderly +manner. During each interview session, *HireLah!* helps the user to keep track of interviewee information and interview +rubrics in one central location, making the interview process more efficient. +{empty} + +{empty} + + +*HireLah!* has the following main features: + +. Manage multiple interview sessions. +. Manage interview rubrics for each interview session, including: +.. Attributes to look out for in interviewees. +.. Questions to ask interviewees. +.. Metrics to rank interviewees based on their performance. +. Manage remarks and score entered by the user during an interview. +. Rank interviewees based on particular sets of attributes or a metric after interviews. +. Export interview transcripts as reference. + +=== Purpose and Audience +This Developer Guide specifies the architecture, design and implementation of *HireLah!*, as well as our considerations +while designing this app. + +It is intended for developers, software testers, and people who would like to enhance this project in +one way or another to understand *HireLah!* on a deeper level. == Setting up @@ -34,7 +60,9 @@ The *_Architecture Diagram_* given above explains the high-level design of the A The `.puml` files used to create diagrams in this document can be found in the link:{repoURL}/docs/diagrams/[diagrams] folder. Refer to the <> to learn how to create and edit diagrams. -`Main` has two classes called link:{repoURL}/src/main/java/seedu/address/Main.java[`Main`] and link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp`]. It is responsible for, + +`Main` has two classes called link:{repoURL}/blob/master/src/main/java/hirelah/Main.java[`Main`] and link:{repoURL}/blob/master/src/main/java/hirelah/MainApp.java[`MainApp`]. It is responsible for, + * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup method where necessary. @@ -64,31 +92,53 @@ image::LogicClassDiagram.png[] [discrete] ==== How the architecture components interact with each other -The _Sequence Diagram_ below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The _Sequence Diagram_ below shows how the components interact with each other for the scenario where the user issues the command `add attribute leadership`. -.Component interactions for `delete 1` command +.Component interactions for `add attribute leadership` command image::ArchitectureSequenceDiagram.png[] The sections below give more details of each component. +// tag::ui[] [[Design-Ui]] === UI component .Structure of the UI Component image::UiClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/ui/Ui.java[`Ui.java`] +*API* : link:{repoURL}/blob/master/src/main/java/hirelah/ui/Ui.java[`Ui.java`] -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `SessionPanel`, +`InterviewPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. -The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/src/main/java/seedu/address/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`] +The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` +files that are in the `src/main/resources/view` folder. For example, the layout of the +link:{repoURL}/blob/master/src/main/java/hirelah/ui/MainWindow.java[`MainWindow`] is specified in +link:{repoURL}/blob/master/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`]. The `UI` component, -* Executes user commands using the `Logic` component. +* Executes user commands using the `Logic` component through the `CommandExecutor` functional interface. +* After executing each command, performs command-specific view updates as instructed + by the CommandResult returned by that command. * Listens for changes to `Model` data so that the UI can be updated with the modified data. +When the app is launched, `SessionPanel` is displayed to allow the user to manage interview sessions. When an interview +session is selected, `SessionPanel` becomes hidden, and the `InterviewPanel` is displayed to allow the user to conduct +interviews. This process is summarised in <>. The structure of the `InterviewPanel` is shown in <>. + +[[SwitchPanel]] +.Activity Diagram showing the toggling between SessionPanel and InterviewPanel +image::UiSwitchPanelActivityDiagram.png[] + +[[InterviewPanel]] +.Structure of the InterviewPanel used to display an interview session +image::UiInterviewPanelClassDiagram.png[] + +// end::ui[] + [[Design-Logic]] +// tag::logic[] === Logic component [[fig-LogicClassDiagram]] @@ -96,151 +146,519 @@ The `UI` component, image::LogicClassDiagram.png[] *API* : -link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`] +link:{repoURL}/blob/master/src/main/java/hirelah/logic/Logic.java[`Logic.java`] -. `Logic` uses the `AddressBookParser` class to parse the user command. + +. `Logic` uses either the `NormalParser` or `InterviewParser` class to parse the user command, depending on the app phase stored in the Model. . This results in a `Command` object which is executed by the `LogicManager`. -. The command execution can affect the `Model` (e.g. adding a person). +. The command execution can affect the `Model` (e.g. adding an attribute). +. The command execution may also call the storage to save the Model if the command modified the Model. . The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. -. In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying help to the user. +. The `CommandResult` object then instructs the `Ui` to perform certain actions, such as displaying the +list of the current attribute. `CommandResult` is subclassed to provide polymorphism on the UI actions performed. + +Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("add attribute leadership")` API call. -Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call. +[[AddSequenceDiagram]] +.Interactions Inside the Logic Component for the `add attribute leadership` Command +image::AddSequenceDiagram.png[] -.Interactions Inside the Logic Component for the `delete 1` Command -image::DeleteSequenceDiagram.png[] +NOTE: The lifeline for `AddCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. + +The exact details of how `AddAttributeCommand` gets the `AttributeList` from `Model`, then modifies the list, then passes the `AttributeList` to `Storage` is left +out in this diagram. -NOTE: The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +// end::logic[] +// tag::model[] [[Design-Model]] === Model component .Structure of the Model Component image::ModelClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/model/Model.java[`Model.java`] +*API* : link:{repoURL}/blob/master/src/main/java/hirelah/model/Model.java[`Model.java`] The `Model`, * stores a `UserPref` object that represents the user's preferences. -* stores the Address Book data. -* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. -* does not depend on any of the other three components. - -[NOTE] -As a more OOP model, we can store a `Tag` list in `Address Book`, which `Person` can reference. This would allow `Address Book` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object. An example of how such a model may look like is given below. + - + -image:BetterModelClassDiagram.png[] - +* stores the session data including questions, attributes, metrics, and interviewees including their interview transcripts. +* stores the current app state, including whether the user has finalised the attributes and questions, the current phase that + the app is in (pre-session, in the normal phase or in an interview), the current session and the current interviewee in focus, + whether being interviewed or whose report is being viewed. +* exposes an unmodifiable `ObservableList`, `ObservableList`, `ObservableList`, `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +// end::model[] + +// tag::storage[] [[Design-Storage]] === Storage component .Structure of the Storage Component image::StorageClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/storage/Storage.java[`Storage.java`] +*API* : link:{repoURL}/blob/master/src/main/java/hirelah/storage/Storage.java[`Storage.java`] +// tag::storage[] The `Storage` component, -* can save `UserPref` objects in json format and read it back. -* can save the Address Book data in json format and read it back. +* contains the various classes of *Storages* to support the following features. +** `UserPrefsStorage` saves `UserPref` objects in json format and read it back. +** `ModelStorage` saves the `Model` data in json format and read it back. +** `IntervieweeStorage` saves `IntervieweeList` to interviewee.json and read it back. +** `AttributeStorage` saves `AttributeList` to attribute.json and read it back. +** `QuestionStorage` saves `QuestionList` to question.json and read it back. +** `MetricStorage` saves `MetricList` to metric.json and read it back. +** `TranscriptStorage` saves `Transcripts` of individual interviewees to separate json files in /transcript +- for example an interviewee with id = 1 has his/her transcript saved to transcript/1.json + + +.Structure of the IntervieweeStorage Component +image::IntervieweeStorageClassDiagram.png[] + +*API* : link:{repoURL}/src/main/java/hirelah/storage/Storage.java[`IntervieweeStorage.java`] + +The `IntervieweeStorage` component, +is responsible for saving an `IntervieweeList`. +An `Interviewee` contains a `Transcript` which holds contain the `RemarkList` and a `Hashmap` of `Attribute` to score. + +During the interview, the interviewee will rate certain desirable `Attribute` with a certain score, this is stored in +the `Hashmap`.`RemarkList` contains all the `Remarks` for the `Interviewee` during the interview. + +The interviewer may give a particular `Remark` based on certain `Question`, hence the `RemarkList` would depend on the +`QuestionList` to check for its validity. + +There is also a need to check for the validity of `Hashmap` by checking the `Attribute` given in the `Hashmap` against the +`AttributeList`, which result in the dependency between `JsonSerializableTranscript` and `AttributeList`. + + +.Structure of the AttributeStorage Component +image::AttributeStorageClassDiagram.png[] + +*API* : link:{repoURL}/src/main/java/hirelah/storage/Storage.java[`AttributeStorage.java`] + + +All the `Attribute` are being stored as a list which is saved in a json file. When converting the json file back +their `Attribute` Object, the `JsonSerializableAttributes` and `JsonAdaptedAttributes` classes are being deployed. + +The other types of storages contains the identical structure similar to `AttributeStorage` , they +are being omitted. +// end::storage[] [[Design-Commons]] === Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `hirelah.commons` package. == Implementation This section describes some noteworthy details on how certain features are implemented. -// tag::undoredo[] -=== [Proposed] Undo/Redo feature +// tag::sessionFeature[] +=== Session Feature + +HireLah! differs from AddressBook significantly in that a single user will likely have many sessions, +as compared to just owning a single address book. The app thus provides means of managing multiple sessions from +within the app itself, instead of having to change `preferences.json` to create a new session or switch between sessions. + ==== Proposed Implementation -The undo/redo mechanism is facilitated by `VersionedAddressBook`. -It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. -Additionally, it implements the following operations: +Having multiple sessions and changing between them from within the app means that HireLah! cannot load data from storage +on app startup. -* `VersionedAddressBook#commit()` -- Saves the current address book state in its history. -* `VersionedAddressBook#undo()` -- Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` -- Restores a previously undone address book state from its history. +Instead, when the app starts, the ModelManager does not have its components (`IntervieweeList`, `AttributeList`, etc.) loaded, +only `UserPrefs`, and the AppPhase is set to `PRE_SESSION`. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +UI displays the `SessionPanel` which shows the available sessions in the "sessionsDirectory" folder. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +In `PRE_SESSION` phase, logic uses the `PreSessionParser` which accepts commands to open an existing session or create a new one. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Once a session is chosen, the command calls `Storage#loadSession` which creates new Storage components for the new session +(`IntervieweeStorage`, `AttributeStorage` etc.) that save to the correct session directory. `loadSession` then calls +`Storage#initModelManager`, which loads new Model components (`IntervieweeList`, `AttributeList` etc.), then replaces +the current `ModelManager` components. `AppPhase` is then set to `NORMAL` at which the app starts its usual behavior (adding interviewees etc.). -image::UndoRedoState0.png[] +Finally after refreshing the `ModelManager`, then a new UI `InterviewPanel` is created that observes the new `Model` components. +Since the Model's components have been replaced (a different set of ObservableLists), the UI can only be created at this point else the UI +would be data binding to the wrong lists. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +The following activity diagram shows the sequence of initialization of components when a session is opened. -image::UndoRedoState1.png[] +image::SessionActivityDiagram.png[] -Step 3. The user executes `add n/David ...` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +From the `NORMAL` phase, the close session command can be given, which simply un-sets the current session in `Model` and returns the +AppPhase to `PRE_SESSION`. Thus the app is ready to open a new session, by resetting the storage components and the Model components, +then creating a new UI `InterviewPanel` again. -image::UndoRedoState2.png[] +==== Design Considerations -[NOTE] -If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +===== Aspect: How sessions are stored in the app + +* **Alternative 1 (current choice):** Do not store information about sessions in Model. Directly read available sessions +from the `/data` directory (or whatever the user set the "sessionsDirectory" to in `preferences.json`). Scan the +directory again every time the SessionPanel is displayed. +** Pros: Information about the available directories is always synchronized with the filesystem. +It is possible to copy a session from somewhere else into the "sessionsDirectory" and the app will detect it. +** Cons: Simplistic - app naively treats all directories in the "sessionsDirectory" directory as sessions and displays +them as available sessions to the user. If a folder is created externally with invalid data, it will also be treated +as a session, only failing when the user tries to load it. +* **Alternative 2:** Model contains a `SessionList` which tracks what sessions have been created or deleted. +** Pros: User cannot create/delete sessions outside the app, changes to the sessions (new session/delete session) +can be tracked within the app itself rather than repeatedly making IO calls to the filesystem. +** Cons: There is no "single source of truth" as both the file system and the app have a list of sessions, and it is not +straightforward to ensure both are synchronized, eg. if a session data directory is deleted on the file system, the app +will not be aware of it. + + +===== Aspect: How session data is loaded and saved + +* **Alternative 1 (current choice):** Load session only when a command is given to open a session from a directory. +** Pros: Most user friendly, managing sessions is performed through the same CLI. Memory efficient - data is loaded only +exactly when needed. +** Cons: Complex to implement +* **Alternative 2:** Load all data for all sessions into memory when the app starts, open session +merely selects the current session in focus and displays UI with current session data. +** Pros: Straightforward to implement (All data can be loaded on app starting, need not change implementation from +AddressBook). Switching between sessions is very straightforward. +** Cons: Will be memory intensive as all data even from non-active sessions and past sessions will be loaded. As HireLah! +may include large amounts of data in interview Transcripts, loading all the Transcripts from previous sessions will likely +negatively impact startup timing. Furthermore, it is unlikely that an interviewer needs to access previous interview sessions +data in a current interview, making a lot of the memory consumption wasteful. +** Relevance consideration: It is not often that an interviewer would need to switch between sessions while interviewing for a +specific thing, for example a role in the company. +* **Alternative 3:** Require the user to configure the session to load before app starts up, in `preferences.json` +** Pros: Simplest to implement, yet memory efficient as only the relevant data is loaded +** Cons: Not user friendly - functionality cannot be performed within the app. User cannot discover the functionality on how +to start a new session without consulting the user guide. +// end::sessionFeature[] + +// tag::validstate[] +=== State Validity and Finalise Feature +==== Description +HireLah! can process numerous different inputs from the user, starting from adding an interviewee, adding a metric, +opening the report of an interviewee, etc. However, not all inputs are valid at any point of time. So, we need to make +sure that the right commands are accepted at the right phase. In addition, we also need to check that the input received +is also valid such that the Model component of our application is always in a valid state. + +A feature in our application that supports this state validity is the Finalise feature, the feature used to +finalise the attributes and questions of an interview session, after the user has finished their CRUD operations to the +attributes and questions. After finalising, the user can no longer change the attributes and questions of this interview +session. + +==== Implementation + +Validity of our application state is achieved through three ways, which includes: + +* Separation of the application into 3 different phases +* Command validation by the Parser and Model component +* Implementation of Finalise feature + +===== Separation of the application into 3 different phases +There are 3 different phases in our application, pre-session, normal, and interview phase. At any point of time, our +application is in one of these 3 states, and this information is stored inside the model, and it can be retrieved by calling +`model.getAppPhase()`. The reason behind the separation of the application into 3 different phases is because we need to prevent the user from +entering invalid commands at any point of time. For example, you cannot start interviewing person B while you are +interviewing person A. You need to end your interview with person A then you can interview person B. + +When the application had just opened, it will immediately enter into the `pre-session phase`, where the user are not allowed +to do anything except creating a new session or opening other previously-made sessions. + +When the user had entered into a session, the application enter the `normal phase`. In this phase, the user can prompt the application +to execute all of the features in the application, except opening a new session and adding interview-related information +specific to an interviewee (e.g. adding a remark to person A). + +When the application is in the `normal phase` and the user decided to interview a particular interviewee, the application will +enter to the `interview phase`. During this phase, the user are only allowed to execute commands related to the interview process +of an interviewee, which includes adding remark, scoring the attribute and adding remarks as the answer to a particular question, +and toggling view between attributes, questions, or metrics. + +The implementation of these 3 different phases is achieved through the presence of 3 different `parsers` in our application. +After retrieving the `current phase` of the application from the `Model`, the `Logic` component will then decide which `parser` of these 3 +should be used to parse the input. Through this, we make sure that only appropriate and valid `commands` at a particular `phase` +are accepted at any point of time. + +Given below are several examples of usage scenarios when the user prompts for some commands at a certain phase of the application: + +Scenario 1. When the user provides a `command` which is not recognized by the `parser` that is associated with the +current `phase` of the application, the respective `parser` will throw a `ParseException`, informing the user that this +command is unknown. Suppose the app is currently at the `PreSession phase`, and the user tries to add a new interviewee, +`add interviewee Jane Doe`. + +image::SessionScenario1SequenceDiagram.png[] + +Scenario 2 (Ideal Scenario). When the user provides a `command` which is recognized by the `parser` that is associated with the +current `phase` of the application, the respective `parser` will parser the `command` and execute the respective `CommandResult` +to the model. For an example, see <>. + +===== Command Validation by the Parser and Model component + +By the separation of the application into 3 different phases, we had make sure that the input that is accepted at any particular +point if time are input that are appropriate at the current application `phase`. However, this does not guarantee that this +input will be valid according to the current state of the `Model`. For example, if the application is currently at the `normal phase`, +it will be able to accept the `command` to interview an interviewee. However, if this interviewee has been interviewed before, +doing an interview to the same person should not be allowed. + +Assuming that the prompt inserted by the user is accepted at the `current phase` of the application, given below are +several examples of usage scenarios: + +Scenario 1. When the user provides a `command` that is recognized by the `current phase` of the app but it is missing compulsory fields , +the respective `parser` will check against it and throw `ParseException`, informing that the prompt is incomplete. For an example, see +<>. -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Scenario 2. When the user provides a complete and valid command that is recognized by the `current phase` of the application +but is not valid for the current state, the `Model` will check the command and throw a `CommandException`, informing the user +that this `command` is invalid for the current state. For an example, see <>. -image::UndoRedoState3.png[] +Scenario 3 (Ideal Scenario). When the user provides a complete and valid `command` that is recognized by the `current phase` of the +application and is valid for the current state. For an example, see <>. -[NOTE] -If the `currentStatePointer` is at index 0, pointing to the initial address book state, then there are no previous address book states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo. +===== Finalise Feature + +Even with all of these implementations of `command` accepting according to the `current phase` and `command` checking in a +particular `phase`, there is another possibility of invalid state in our application, which happens through the modification +of the `interview session's` `attributes` and `questions`. For example, a metric which takes in `attribute` `leadership` and `integrity` +has been created. Later, the user can possibly delete the `attribute` `leadership`. This makes the metric create not valid anymore. -The following sequence diagram shows how the undo operation works: +Thus, we need a feature that can indicate that the `interview session` has been `finalised`, which means that all CRUD operations +to the `attributes` and `questions` of this `interview session` is disallowed. CRUD operations to the `attributes` and `questions` +are originally accepted in the `normal phase` and if the CRUD operation is valid. However, if the application has entered the +`finalised` state, this disablement happens regardless of the validity of the phase and command. -image::UndoSequenceDiagram.png[] +The `finalised` state can be achieved through the `Finalise Command` when the user inputs the word `finalise`. Before the application +is `finalised` the user is not allowed to hold any interview with any interviewee, create any metric and find any best interviewees. +If the application has reached `finalised` state, the user are allowed to do so. -NOTE: The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +Assuming that the user is currently on the `normal phase`, the phase which accepts CRUD operations for the `attributes` +and `questions`, and assuming that the given CRUD operation is valid (e.g. `delete attribute leadership` and +`attribute` `leadership` was there previously), given below are several examples of usage scenarios when the user prompts are related to Finalise Feature: -The `redo` command does the opposite -- it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +Scenario 1. If the application has not been finalised `finalised` and the user tries to interview an interviewee, +`interview Jane Doe`, the `Normal Parser` will parse the input and create an instance of `StartInterviewCommand`. +The `LogicManager` will try to execute this `StartInterviewCommand` toward the `model`, but because it has not been `finalised`, +it will throw a `CommandException` error. -[NOTE] -If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone address book states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +image::FinaliseScenario1SequenceDiagram.png[] + +Scenario 2. If the application has been `finalised` and the user tries to do add a new `attribute`, `add attribute leadership`, +the `Normal Parser` will parse the input and create an instance of `AddAttributeCommand`. The `LogicManager` will try to execute +this AddAttributeCommand toward the `model`, but because it has been `finalised`, it will throw a `CommandException` error. + +image::FinaliseScenario2SequenceDiagram.png[] + +Scenario 3 (Ideal Scenario). If the application has been `finalised` and the user tries to interview an interviewee, +`interview Jane Doe`, the `Normal Parser` will parse the input and create an instance of `StartInterviewCommand`. +The `LogicManager` will execute this `StartInterviewCommand` toward the `model`, and the result is returned. + +image::FinaliseScenario3SequenceDiagram.png[] + +Scenario 4 (Ideal Scenario). If the application has not been `finalised` and the user tries to do add a new `attribute`, +`add attribute leadership`, the `Normal Parser` will parse the input and create an instance of AddAttributeCommand. +The `LogicManager` will execute this AddAttributeCommand towards the `model` and the result is returned. + +image::FinaliseScenario4SequenceDiagram.png[] + +The following activity diagram shows the sequence of activities happening after `finalise` command. + +image::FinaliseActivityDiagram.png[] + +==== Design Considerations + +===== Aspect: Maintaining the consistency of application state + +* **Alternative 1 (current choice):** Have `finalise` command and disallow CRUD operation to `attributes` and `questions` +** Pros: There will no be any invalid values or states, like an `attribute` score assigned to null. +** Cons: Once the user decide to `finalise`, the user can no longer edit the `attributes` and `questions`. THe user will +need to create a new session if the user wants to. +* **Alternative 2:** Do not have a `finalise` command. User can perform CRUD operation to `attributes` and `questions` at any time, +including after interview has been done or some metrics have been created. Deleted `attributes` and `questions` will be removed +from any part of the application, and newly created `attributes` will have null score and newly created `questions` +will be assigned to no answers. +** Pros: More flexibility for the user if the user wants to change the `attributes` and `questions`. +** Cons: The user might not be aware about the consequence of deleting one `attribute` or `question`, which might lead +to deletion of the all the `metrics` that uses this `attribute `, removal of all `remarks` associated as the answer of a `question`. +As new `attribute` are assigned to null score, these interviewees could not be compared with the others with regards to this feature +when the user is using the `best interviewee` feature. +User might need to assign the score value of this newly added `attribute` to every single `interviewees` that has been interviewed +before this addition, which is a tedious and unfriendly operation. +* **Alternative 3:** Do not have a `finalise` command. User can perform CRUD operation to `attributes` and `questions` at any time, +including after interview has been done or some metrics have been created. Deleted `attributes` and `questions` will be removed +from any part of the application, and newly created `attributes` will have a default score and newly created `questions` +will be assigned to no answers. +** Pros: More flexibility for the user if the user wants to change the `attributes` and `questions`. +** Cons: The user might not be aware about the consequence of deleting one `attribute` or `question`, which might lead +to deletion of the all the `metrics` that uses this `attribute `, removal of all `remarks` associated as the answer of a `question`. +As new `attribute` are assigned to default score, the result of comparison between this `interviewee` and the others might +not be accurate anymore. +// end::validstate[] + + +// tag::findbestfeature[] +=== Find Best Interviewees Feature +==== Description +When an interviewer wants to take a decision on hiring interviewees, it will be cumbersome for the interviewer to take +a look of the interviewees' score, to take a decision, especially when it comes to a large scale hiring pool. Therefore, +HireLah! provides a Find Best Interviewees feature to show the top interviewees, depending on how many employees the +company need, and what kind of employees does the company desire. + +==== Implementation + +The Find Best Interviewee feature is facilitated by the `BestCommand`. It has the following fields: + +* `numberOfInterviewees`: The number of interviewees that the interviewer wants to hire +* `paramPrefix`: The prefix of the parameter that wants to be used as a comparator between interviewees. +* `paramType`: The type of parameter that wants to be used by the interviewer. The possible values are the following: +`OVERALL`, `METRIC`, and `ATTRIBUTE`. + +Given below are several examples of usage scenarios when the user prompts for Best Interviewees: + +[[Scenario-1-Best-Interviewee-Feature]] + +Scenario 1. When the user provides a command with incomplete compulsory fields (e.g. number of interviewees), the +`BestCommandParser` will throw `ParseException`, informing that the size provided is not an integer because the parser +takes the preamble of the command to get the value for `numberOfInterviewees`. + +image::FindBestScenario1SequenceDiagram.png[] + +[[Scenario-2-Best-Interviewee-Feature]] + +Scenario 2. When the user provides a command with valid fields, yet there are no interviewees that has been interviewed, +a `CommandException` is thrown instead, informing that there is no interviewee that has been interviewed, thus no result +can be shown. + +image::FindBestScenario2SequenceDiagram.png[] + +Scenario 3. When the user provides a command with multiple parameters that want to be used for a comparator, a `ParseException` +is thrown instead, informing that the user has provided more than 1 parameter. + +image::FindBestScenario3SequenceDiagram.png[] + +[[Scenario-Ideal-Best-Interviewee-Feature]] + +Scenario 4 (Ideal Scenario). Here is the Sequence Diagram for `FindBestCommand` for an ideal case (minor method calls are omitted): -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +image::FindBestSequenceDiagram.png[] -image::UndoRedoState4.png[] +===== Getting the best interviewees from Model +To obtain the list of best interviewees, the `BestCommand` has a private method called `getBest` which takes in a `model`, +a `comparator`, and `size`. The `getBest` method retrieves the best interviewees using an Iterator design pattern. It does +the following: -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow. +* Retrieve `intervieweeList` and `bestNIntervieweeList` from `model` +* Clear the current `bestNIntervieweeList` +* Since the `comparator` only compares interviewees that have been interviewed, a filter operation needs to be done to +filter out interviewees that have not been interviewed +* Sort the filtered interviewees based on the `comparator` +* Insert the first `size` interviewees to the `bestNIntervieweeList` -image::UndoRedoState5.png[] +There are cases where `getBest` method does not reflect exactly the number of interviewees that the user entered: -The following activity diagram summarizes what happens when a user executes a new command: +* The number of interviewees that has been interviewed is less than the number of interviewees the user prompted. In +this case, all the interviewed interviewees will be shown, in a sorted order based on their score. Therefore, the number +of interviewees shown will be less than what the user entered. -image::CommitActivityDiagram.png[] +* There are ties between interviewees at the cut-off position. For example, a case where the fifth interviewee, and the +sixth interviewee have the same scores, while the user prompts for the best 5 interviewees. In this case, the sixth interviewee +will also be shown. Therefore, the number of interviewees shown will be more than what the user entered. + +Below is the activity diagram to summarize and show how `getBest` method works: + +image::GetBestActivityDiagram.png[] ==== Design Considerations -===== Aspect: How undo & redo executes +===== Aspect: How to display the best interviewees -* **Alternative 1 (current choice):** Saves the entire address book. +* **Alternative 1 (current choice):** Clears the current content of best interviewees, then add the +best interviewees one by one. ** Pros: Easy to implement. -** Cons: May have performance issues in terms of memory usage. -* **Alternative 2:** Individual command knows how to undo/redo by itself. -** Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). -** Cons: We must ensure that the implementation of each individual command are correct. +** Cons: Have a slightly lower execution time. +* **Alternative 2:** Creates a new ObservableList for the best interviewee list, then change the Model's +best interviewees list to point to this list, and the MainWindow's BestIntervieweeListPanel. +** Pros: Have a slightly faster theoretical execution time. +** Cons: The implementation is slightly trickier compared to alternative 1 since it involves multiple components of the app. + +// end::findbestfeature[] + +// tag::uicommand[] +=== Updating UI elements after user commands + +==== Description +A lot of things goes on during the interview process. As such, HireLah! needs to handle different types of input commands +from the user and update the GUI accordingly in response. AddressBook3, which HireLah! is based on, uses the `CommandResult` +class to achieve this. Based on our design of HireLah! however, there are three types of command outcomes that the user's +input may give: + +. Toggle to see a different list of information, for example toggling from the `SessionPanel`, which lists out the information +of all interview sessions created on HireLah!, to `InterviewPanel`, which shows the details of one interview session. + +. Scroll to a particular entry in a list. This is required for the `goto` command, which allows the user to see an +interviewee's remarks at a particular timestamp by scrolling the `RemarkList` to a particular index. + +. Simply printing out a message in the `ResultDisplay` to show the user. + + +==== Implementation +In order to handle all three types of command outcomes, we have decided to extend the original `CommandResult` class to +produce two new subclasses: `ToggleCommandResult`, which handles type 1 outcomes, and `NavigationCommandResult`, which +handles type 2 outcomes. The original `CommandResult` class is used to handle type 3 outcomes. + +In addition, we have implemented the `ToggleView` enum, which is returned as an attribute in `ToggleCommandResult` to inform +the UI on the UI component to toggle to. + +Given below is a class diagram to summarise the characteristics of these three CommandResult classes, as well as the +ToggleView enum: + +image::CommandResultClassDiagram.png[] + +==== Design Considerations + +===== Aspect: How to design and update the UI components +`ToggleCommandResult` also signals that the relevant UI component needs to be updated, and decisions have to be made on +how the implicated UI components should be updated -===== Aspect: Data structure to support the undo/redo commands +* *Alternative 1*: Reinitialise the entire UI after every command +** Pros: This will definitely update the changes required of the UI accordingly, as the new UI can be built to reflect the +current state of the `Model`. +** Cons: This method is extremely inefficient, and may slow down the application considerably when the user has entered a +lot of information. +* *Alternative 2*: Update only the UI component affected if necessary, for eg `IntervieweeListPanel` +** Pros: This is a lot more efficient compared to Alternative 1. +** Cons: Some commands, such as `:end` which ends an interview with an interviewee needs multiple UI components to be updated, +such as `IntervieweeListPanel`, `RemarkList` and `SessionInformationCard`, and this method only updates `IntervieweeListPanel` +as this command returns the ToggleView enum INTERVIEWEE. -* **Alternative 1 (current choice):** Use a list to store the history of address book states. -** Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project. -** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedAddressBook`. -* **Alternative 2:** Use `HistoryManager` for undo/redo -** Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase. -** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. -// end::undoredo[] +Current Implementation: We have taken a compromise between the two approaches. UI components that are often updated together +are grouped together, such as `IntervieweeListPanel` and `SessionInformationCard`, such that they are always updated together. +In addition, we made use of Observable classes to reduce the number of times an UI component needs to be reinitialised. -// tag::dataencryption[] -=== [Proposed] Data Encryption +// end::uicommand[] -_{Explain here how the data encryption feature will be implemented}_ +// tag::storage[] +=== Saving of the Interviewee -// end::dataencryption[] +==== Description +The `Interviewee` class contains various fields, one of the more troublesome fields to save is the `Transcript` +of the `Interviewee` as it contains the `RemarkList` and a `Hashmap` of `Attribute` to score, which is stored as +`Double`. Additionally, `Interviewee` can only have `Transcript` once they have been interviewed and when the model is finalised. +Hence, our group decided to split the saving of the `Transcript` from other fields of the the Interviewee such as the +name of the `Interviewee`. + +Furthermore, since each interviewee contains their own Transcript, which contains a list of remarks which may potentially +become very long (if an interview lasts for an hour, there could be hundreds or thousands of remarks), it would harm performance +if every change to the interviewee list required saving of each interviewee and their entire transcript. + +Note that this is also unnecessary since the only time a transcript is changed and thus saved is during an interview. +The saving of the transcript can thus be effectively decoupled from the interviewee class. + +==== Implementation + +To achieve this, we make use of the proxy class `JsonAdaptedInterviewee` which acts as a data carrier between +the interviewee stored in json and the actual Interviewee class. This class can be constructed from an Interviewee, +and has a `toModelType` method which converts it back to a Interviewee. + +`JsonAdaptedInterviewee` does not store the transcript object. Instead it contains a boolean flag "transcript", which +indicates if a interviewee has a transcript. This allows toModelType to load said transcript if present from a different file, +thus restoring the orignal Interviewee, without having to save the interviewee together with the transcript. + +// end::storage[] === Logging @@ -279,7 +697,8 @@ Refer to the guide <>. *Target user profile*: -* has a need to manage a significant number of contacts +* an interviewer, or anyone who needs to conduct interviews +* has a need to manage and a large number of interviewees, their resumes and interview transcripts in an organized manner. * prefer desktop apps over other types * can type fast * prefers typing over mouse input @@ -295,131 +714,1054 @@ Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (un [width="59%",cols="22%,<23%,<25%,<30%",options="header",] |======================================================================= |Priority |As a ... |I want to ... |So that I can... -|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the App +|`* * *` |New Interviewer |See usage instructions | Learn to use HireLah! -|`* * *` |user |add a new person | +|`* * *` |Interviewer |Create a new interview session | Initialise the interviewee, their details, attributes and +questions specific to this interview session. -|`* * *` |user |delete a person |remove entries that I no longer need +|`* * *` |Interviewer |Add a new interviewee to an interview session | Keep track of interviewees applying for a job opening -|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list +|`* * *` |Forgetful Interviewer |View the list of interviewees and their interview status|Remember their names and interview those who have not been interviewed -|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident +|`* * *` |Interviewer |Delete an interviewee from an interview session|Remove interviewees who withdrew their job application -|`*` |user with many persons in the address book |sort persons by name |locate a person easily -|======================================================================= +|`* *` |Interviewer |Update the information of interviewees |Ensure that I have the most up to date information about the interviewees + +|`* *` |Interviewer |Add the interviewees' resumes in the app |Not need to manage the resumes externally, possibly missing out on some interviewees and making it more difficult to access + +|`* * *` |Interviewer |Make a list of attributes|Remind myself of what to look out for in the interviewees while interviewing them + +|`* * *` |Interviewer |Modify the list of attributes|Update the interview session's rubrics as needed + +|`* * *` |Interviewer |Make a list of questions to ask|Ask each interviewee the same set of questions + +|`* * *` |Fickle minded Interviewer |Modify the list of questions|Make necessary changes if I decide I want to ask different questions. + +|`* *` |Busy Interviewer |Have an easy way to refer to each interviewee |Do not have +to remember the full name / ID of each interviewee + +|`* * *` |Interviewer |Finalise the attributes and questions for an interview session|Assess all interviewees fairly based on the same attributes and asked the same questions + +|`* * *` |Interviewer |See the list of attributes and questions during an interview|Refer back +to the list of attributes and questions and assess all interviewees according to these exact parameters. + +|`* * *` |Interviewer |Record the remarks of my interviewees during the interview session|Recall details that happened during the interview -_{More to be added}_ +|`* * *` |Interviewer |Indicate when a question was asked during the interview |Assess and review the interviewee’s +answers to a particular question + +|`* * *` |Interviewer |Score the interviewee for each attribute during the interview| Have some basis to compare interviewees later. + +|`*` |Interviewer |Have an audio recording for every interview session |Refer back to it to minimize missing details + +|`* * *` |Interviewer |Open the interview transcript of an interviewee after interviewing him/her +|Recall my impressions of the interviewee when making decisions on who to select. + +|`* *` |Interviewer |Easily find the remarks I made at some time during the interview |Not need to slowly scroll through the entire transcript. + +|`* * *` |Interviewer |Jump to the point where each question was asked |Focus on the important parts of the interview. + +|`* * *` |Interviewer |Find the best few interviewees based on their scores for the attributes |Narrow down the selection when making the decision. + +|`* * *` |Interviewer |Find the best few interviewees based on an attribute |Narrow down the selection when making the decision. + +|`* * *` |Interviewer |Find the best few interviewees based on a custom metric |Value certain attributes over others and give the most weightage to the most critical characteristics. + +|`*` |Interviewer |Play audio of a recording of an interviewee's interview session at a given time| +Recall what the interviewee said. + +|`*` |Interviewer |Visualise the score of attributes of all interviewees|To make easy visual comparisons. + +|`* *` |Interviewer |Export the full report of each interviewee|Share the information with others. + +|`* *` |Interviewer |App to be password protected |Protect sensitive information from prying eyes + +|`* *` |Experienced Interviewer |Perform all tasks from the keyboard |Not waste time moving between the cursor and the keyboard, especially while taking notes. + +|======================================================================= [appendix] == Use Cases -(For all use cases below, the *System* is the `AddressBook` and the *Actor* is the `user`, unless specified otherwise) +(For all use cases below, the *System* is the `HireLah!` and the *Actor* is the `User`, unless specified otherwise) [discrete] -=== Use case: Delete person +=== Use case: UC01 - Create new Session *MSS* -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User chooses to create a new Interview Session +2. User provides a name for the Session (eg. CEO Interview) +3. HireLah! creates the new Session and saves it +4. HireLah! automatically pass:[opens the Session (UC02)] + Use case ends. *Extensions* [none] -* 2a. The list is empty. +* 2a. User provides an invalid name or an existing session name. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC02 - Open existing Session + +*MSS* + +1. User chooses to open a previous Interview Session +2. User provides the name of previous session (eg. CEO Interview) +3. HireLah! restores data from the session from memory + Use case ends. -* 3a. The given index is invalid. +*Extensions* + +[none] +* 2a. No such previous session exists. + [none] -** 3a1. AddressBook shows an error message. +** 2a1. HireLah! shows an error message. + Use case resumes at step 2. -_{More to be added}_ +[discrete] +=== Use case: UC03 - Delete session -[appendix] -== Non Functional Requirements +*MSS* -. Should work on any <> as long as it has Java `11` or above installed. -. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +1. User chooses to delete a session. +2. User provides the name of the session to delete. +3. HireLah! deletes all the session data of the given session. +Use case ends. -_{More to be added}_ +*Extensions* -[appendix] -== Glossary +[none] +* 2a. No such previous session exists. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 2. -[[mainstream-os]] Mainstream OS:: -Windows, Linux, Unix, OS-X +[discrete] +=== Use case: UC04 - Add Interviewee -[[private-contact-detail]] Private contact detail:: -A contact detail that is not meant to be shared with others +*Precondition* -[appendix] -== Product Survey +1. User has pass:[opened a session (UC02)] -*Product Name* +*MSS* -Author: ... +1. User chooses to create a new Interviewee. +2. User provides a name, and an alias (optional) for the Interviewee. +3. HireLah! creates the new Interviewee and saves it. ++ +Use case ends. -Pros: +*Extensions* -* ... -* ... +[none] +* 2a. An interviewee with the exact name already exists ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 2. +* 2b. The alias given already refers to another interviewee (either the name or alias) ++ +[none] +** 2b1. HireLah! creates the new Interviewee without the alias. +** 2b2. HireLah! displays an error message regarding the repeated alias. ++ +Use case ends. -Cons: +[discrete] +=== Use case: UC05 - Delete Interviewee -* ... -* ... +*Precondition* -[appendix] -== Instructions for Manual Testing +1. User has pass:[opened a session (UC02)] -Given below are instructions to test the app manually. +*MSS* -[NOTE] -These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. +1. User decides which Interviewee that wants to be deleted from the list. +2. User provides either the full name, the alias, or the ID. +3. HireLah! deletes the interviewee with the following details provided. ++ +Use case ends. -=== Launch and Shutdown +*Extensions* -. Initial launch +[none] +* 2a. There is no interviewee with the given identifier. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 2. -.. Download the jar file and copy into an empty folder -.. Double-click the jar file + - Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +[discrete] +=== Use case: UC06 - Update Interviewee + +*Precondition* + +1. User has pass:[opened a session (UC02)] + +*MSS* + +1. User chooses to edit an interviewee. +2. User provides either the full name, the alias, or the ID. +3. User provides the updated fields, either name, alias or both. +4. HireLah! updates the interviewee information. ++ +Use case ends. + +*Extensions* + +[none] +* 2a. There is no interviewee with the given identifier. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 2. +* 3a. Either the given new name or alias is invalid (a duplicate, or an illegal value) ++ +[none] +** 3a1. HireLah! shows an error message. ++ +Use case resumes at step 2. + +[discrete] +=== Use case: UC07 - Add Attribute + +*Precondition* + +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] + +*MSS* + +1. User chooses a name for the attribute. +2. HireLah! adds the attribute with a given name to the list. ++ +Use case ends. + +*Extensions* +[none] +* 2a. There is already an attribute with the identical name ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC08 - Delete Attribute + +*Precondition* + +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] + +*MSS* + +1. User indicates which attribute to delete, either by full name or by a unique prefix. +2. HireLah! removes the attribute with the given prefix from the list. + ++ +Use case ends. + +*Extensions* + +[none] +* 2a. There is no attribute with the given prefix. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 1. +[none] +* 2b. There are multiple attributes with the same given prefix. ++ +[none] +** 2b1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC09 - Update Attribute -. Saving window preferences +*Precondition* -.. Resize the window to an optimum size. Move the window to a different location. Close the window. -.. Re-launch the app by double-clicking the jar file. + - Expected: The most recent window size and location is retained. +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] -_{ more test cases ... }_ +*MSS* + +1. User indicates the attribute to edit, either by its full name or by a unique prefix. +2. User gives the updated name of the attribute. +3. HireLah! updates the attribute with the given name. ++ +Use case ends. -=== Deleting a person +*Extensions* + +[none] +* 1a. There is no attribute with the given prefix. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes at step 1. +* 1b. There are multiple attributes with the same given prefix. ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case resumes at step 1. +* 2a. The updated attribute name already exists. ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes at step 1. -. Deleting a person while all persons are listed +[discrete] +=== Use case: UC10 - Add Question -.. Prerequisites: List all persons using the `list` command. Multiple persons in the list. -.. Test case: `delete 1` + - Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -.. Test case: `delete 0` + - Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -.. Other incorrect delete commands to try: `delete`, `delete x` (where x is larger than the list size) _{give more}_ + - Expected: Similar to previous. +*Precondition* -_{ more test cases ... }_ +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] -=== Saving data +*MSS* -. Dealing with missing/corrupted data files +1. User chooses to add a question and types out the full question. +2. HireLah! adds the question with the given to the list. ++ +Use case ends. -.. _{explain how to simulate a missing/corrupted file and the expected behavior}_ +*Extensions* -_{ more test cases ... }_ +[none] +* 1a. There is already a question with the identical description. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC11 - Delete Question + +*Precondition* + +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] + +*MSS* + +1. User enters the index of the question that the user wants deleted. +2. HireLah! removes the question with the given index from the list. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. The index given is not within the valid range. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC12 - Update Question + +*Precondition* + +1. User has pass:[opened a session (UC02)] +2. User has not pass:[finalised the session attributes and questions (UC15)] + +*MSS* + +1. User enters an index of the question and the updated question. +2. HireLah! updates the description of the question with the given index. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. The index given is not within the valid range. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use caseL UC13 - Upload Interviewee Resume + +*Precondition* + +1. User has pass:[opened a session (UC02)] + +*MSS* + +1. User chooses to upload the resume of the interviewee the user specifies. +2. User provides the path to the resume file. +3. HireLah! remembers this path. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. HireLah! cannot find the user specified whether by id, alias or full name. +[none] +** 1a1. HireLah! displays an error message. ++ +Use case resumes at step 1. +* 2a. The file specified by the path does not exist. +[none] +** 2a1. HireLah! displays an error message. ++ +Use case resumes at step 1. +* 2b. No file is specified. +[none] +** 2b1. HireLah! shows the User files to choose from. +** 2b2. User chooses a file. ++ +If User cancels the file choosing dialog, HireLah! displays an error message. +Else Use case resumes at step 3. + +[discrete] +=== Use caseL UC14 - Open Interviewee Resume + +*Precondition* + +1. User has pass:[opened a session (UC02)] + +*MSS* + +1. User chooses to open the resume of a specified interviewee. +2. HireLah! opens the resume. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. The identifier provided is not the id, alias or full name of any interviewee. +[none] +** 1a1. HireLah! displays an error message. ++ +Use case resumes at step 1. +* 1b. The identified interviewee does not pass:[have a resume uploaded (UC13)] +[none] +** 1b1. HireLah! displays an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: UC15 - Finalize Questions and Attributes + +*Precondition* + +1. User has opened a session + +*Guarantees* + +1. Attribute list and Question list cannot be changed after finalizing + +*MSS* + +1. User chooses to finalize the current list of questions and attributes +Use case ends + +[discrete] +=== Use case: UC16 - Interview an Interviewee + +*Precondition* + +1. User has pass:[finalized questions and attributes for the session (UC15)]. + +*MSS* + +1. User gives name or alias or id of Interviewee to interview +2. HireLah! displays the interview questions +3. User writes remarks while conducting the interview +4. HireLah! saves the remark and the time during the interview when the remark was made +5. User pass:[records answers to the interview questions (UC17)] +6. User pass:[scores interviewee on each attribute (UC18)] +7. User chooses to end the interview ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Name, alias or id does not refer to any interviewee. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes at step 1. +[none] +* 1b. Interviewee specified has already been interviewed. ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case ends. +[none] +* 7a. User has not scored the interviewee in all attributes ++ +[none] +** 7a1. HireLah! shows an error message. ++ +Use case resumes from step 6. + +[discrete] +=== Use case: UC17 - Record Question Answer + +*Precondition* + +1. User is pass:[interviewing an interviewee (UC16)]. + +*MSS* + +1. User indicates question to record answers for +2. User takes notes of the answer to the question +3. HireLah! saves the remark and the time during the interview when the remark was made ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Question number is invalid (too large, or less than 1) ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case ends. + +[discrete] +=== Use case: UC18 - Score Interviewee + +*Precondition* + +1. User is pass:[interviewing an interviewee (UC16)]. + +*MSS* + +1. User indicates attribute to score +2. User indicates score to give +3. HireLah! overwrites any previous score given with the new score ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Attribute does not exist. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case ends. +[none] +* 2a. Score given is not a number ++ +[none] +** 2a1. HireLah! shows an error message. ++ +Use case resumes from step 1. +* 2b. Score given is out of the range of allowed values (0-10). +[none] +** 2b1. HireLah! shows an error message. ++ +Use case resumes from step 1. + +[discrete] +=== Use case: UC19 - Working with an Interviewee Report + +*Precondition* + +1. User has pass:[stopped an interview session(UC16)] with any interviewee. + +*MSS* + +1. User chooses the interviewee that wants to be examined. +2. User opens the interviewee transcript, containing the remarks that are added during the interview. +3. User may navigate by pass:[questions and time (UC20)] to view their remarks for those questions or at that time. +4. User closes the interviewee report when he/she is done. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. User pass:[has not started an interview (UC16)] with this interviewee. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case ends. +[none] +* 1b. There is no interviewee with a given details (alias, ID, or fullname). ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case resumes from step 1. + +[discrete] +=== Use case: UC20 - Navigating through the Interview report + +*Precondition* + +1. User is pass:[viewing an interview report (UC19)]. + +*MSS* + +1. User provides the time or question number for which he/she wishes to see the remarks made during that period of the interview +2. HireLah! scrolls the interview report to the remark made at the moment specified. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Time provided is too large (beyond the end time) +[none] +** 1a1. HireLah! scrolls to the end of the interview. ++ +Use case ends. +* 1b. Question number provided does not correspond to a question that was answered. +[none] +** 1b1. HireLah! shows an error message. ++ +Use case ends. + + +=== Use case: UC21 - Choose Best Interviewees + +*Precondition* + +1. User has pass:[interviewed at least 1 interviewee (UC16)]. + +*MSS* + +1. User indicates the metric (average, best by single attribute, or user-defined weightage) to sort interviewees by +2. User indicates the number of top interviewees to show +3. HireLah! displays the sorted and filtered list of top candidates ++ +Use case ends. + +*Extensions* + +[none] +* 1a. The indicated metric does not exist ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes from step 1. +[none] +* 2a. The indicated number of interviewees to show is larger than the number of interviewees ++ +[none] +** 2a1. HireLah! sorts and displays all interviewed interviewees in sorted order. ++ +Use case ends. +* 3a. There are ties amongst the interviewees. +[none] +** 3a1. HireLah! includes all ties, even if it exceeds the number specified in step 2. ++ +Use case ends. + +[discrete] +=== Use case: UC22 - Add Metric + +*Precondition* + +1. User has pass:[finalized questions and attributes for the session (UC15)]. + +*MSS* + +1. User chooses the name of the metric and the weight of each attribute +2. HireLah! adds the metric to the list. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Any attribute specified is not in the attribute list. ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case resumes from step 1. +[none] +* 1b. Any weight provided is an invalid number. ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case resumes from step 1. +[none] +* 1c. The name specified is already used for another metric. ++ +[none] +** 1c1. HireLah! shows an error message. ++ +Use case resumes from step 1. + +[discrete] +=== Use case: UC23 - Delete Metric + +*Preconditions* + +1. User has pass:[finalized questions and attributes for the session (UC15)]. + +*MSS* + +1. User indicates the metric to delete by its name, or a unique prefix. +2. HireLah! deletes the metric with the given prefix. + ++ +Use case ends. + +*Extensions* + +[none] +* 1a. There is no metric with the given prefix ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case ends. +[none] +* 1b. There are multiple metrics with the given prefix ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case ends. + +[discrete] +=== Use case: UC23 - Update Metric + +*MSS* + +1. User indicates the metric to edit by its name or a unique prefix. +2. User provides a new name, or an updated list of weights for attributes. +3. HireLah! updates the metric with the new name and/or the new weights. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. There is no metric with the given prefix ++ +[none] +** 1a1. HireLah! shows an error message. ++ +Use case ends. +[none] +* 1b. There are multiple metrics with the given prefix ++ +[none] +** 1b1. HireLah! shows an error message. ++ +Use case ends. +* 2a. The new name is invalid (uses illegal characters or is a duplicate) +[none] +** 2a1. HireLah! shows an error message. ++ +Use case ends. +* 2b. Any attribute specified cannot be found or the weight is not a valid number. +[none] +** 2b1. HireLah! shows an error message. ++ +Use case ends. + +[appendix] +== Non Functional Requirements + +. Should work on any <> as long as it has Java `11` or above installed. +. Should be able to load a session containing up to 1000 interviewees and their interview transcripts within 3 seconds. +. Each command should be intuitively named so the interviewer can get productive with the app without constantly referencing the User Guide. +. The application should be easy to use even for interviewers who have never used command-line programs before. +. The UI design of the application should be intuitive to interviewers to navigate between the different phases of the application. +. The application should not be larger than 100Mb. +. The application should save data after every command and not require interviews to save it manually. +. The application not cause interviewers to lose all their progress if the app crashes in the middle of an interview. The user should be able to + continue the interview where they left off after restarting the app. +. Our code should allow other developers to add new features in the application easily. + +[appendix] +== Glossary + +[[mainstream-os]] Mainstream OS:: +Windows, Linux, Unix, OS-X + +[[private-contact-detail]] Private contact detail:: +A contact detail that is not meant to be shared with others + + +[appendix] +== Instructions for Manual Testing + +Given below are instructions to test the app manually. + +[NOTE] +These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. + +=== Launch and Shutdown + +. Initial launch + +.. Download the jar file and copy into an empty folder +.. Double-click the jar file + + Expected: Shows the GUI with an empty session screen. + +. Shutdown +.. Type `exit` in the command line. + +Expected: The app is closed, with all the data saved. + +=== Create, Open and Close Session +. Test case: `new session ceo` + +Expected: It will create a session named `ceo` and the GUI switches to the normal mode. +. Test case: `new session ceo` then `close session` + +Expected: It will create a session named `ceo` and the GUI switches to the normal mode, then after +executing the second command, it closes the session and goes to the session screen. + +=== Create Interviewees, and Attributes +. Prerequisites: The app is in a session and the session is not finalized yet. +. Test case: `add interviewee Jane` + +Expected: An interviewee named Jane is added to the interviewee list. +. Test case: `add interviewee Dr. Bro the 3rd -aka Bro` + +Expected: An interviewee named Dr. Bro the 3rd with alias Bro is added to the interviewee list. +. Test case: `add attribute nonalphanums123` +Expected: An error message is shown that the attribute cannot be added because it contains non alphabets. +. Test case: `add attribute leadership` +Expected: An attribute named `leadership` is added to the list +. Test case: `add attribute` +Expected: An error message showing that the format is invalid. +. Test case: `add interviewee` +Expected: An error message showing that the format is invalid. + +=== Delete Interviewees, and Attributes +. Prerequisites: The session only have an interviewee named Jane with alias Doe, and id 1, and an attribute named leadership, and +the session is not finalized yet. +. Test case: `delete interviewee Bob` + +Expected: An error message is shown, stating that there is no interviewee identified with Bob. +. Test case: `delete interviewee Doe` + +Expected: The interviewee is deleted from the list. +. Test case: `delete attribute tenacity` + +Expected: An error message is shown, stating that there is no attribute named tenacity +. Test case: `delete attribute lea` + +Expected: The attribute leadership is deleted because lea is a unique prefix for leadership. +. Test case: `delete attribute` +Expected: An error message showing that the format is invalid. +. Test case: `delete interviewee` +Expected: An error message showing that the format is invalid. + +=== Edit Interviewees, and Attributes +. Prerequisites: The session only have an interviewee named Jane with alias Doe, and id 1, and an attribute named leadership, and +the session is not finalized yet. +. Test case: `edit interviewee Bob -n Bobby` + +Expected: An error message is shown, stating that there is no interviewee identified with Bob. +. Test case: `edit interviewee Doe -n John` + +Expected: Changes the name of the interviewee from `Jane` to `John`. +. Test case: `edit interviewee Doe -aka brother` + +Expected: Changes the alias of the interviewee from `Doe` to `brother`. +. Test case: `edit interviewee 1 -n Bob -aka Bobby` + +Expected: Changes the name of the interviewee from `Jane` to `Bob`, and the alias from `Doe` to `Bobby` +. Test case: `edit attribute tenacity -a dignity` + +Expected: An error message is shown because the attribute `tenacity` does not exist. +. Test case: `edit attribute lea -a dignity` + +Expected: The attribute `leadership` is changed to `dignity` because `lea` is a unique prefix for `leadership` in this +case. +. Test case: `edit attribute` +Expected: An error message showing that the format is invalid. +. Test case: `edit interviewee` +Expected: An error message showing that the format is invalid. + +=== Create Questions +. Prerequisites: The session is not finalized yet. +. Test case: `add question what is this question?` + +Expected: A question with a deescription `what is this question?` will be added to the question list. + +=== Edit Questions +. Prerequisites: The session is not finalized yet and there is one question in the session with a description `how are you?` +. Test Case: `edit question 1 -q what ya doin?` + +Expected: The question's description will be changed to `what ya doin?`. +. Test Case: `edit question -1 -q what ya doin?` + +Expected: An error message is shown because there is no question with number `-1`. + +=== List Interviewees, Questions, and Attributes +. Prerequisites: The app is already in a session +. Test case: `attributes` + +Expected: Shows all the attributes that have been added in the session. They are shown at the right panel of the GUI. +. Test case: `questions` + +Expected: Shows all the questions that have been added in the session. They are shown at the right panel of the GUI. +. Test case: `interviewees` + +Expected: Shows all the interviewees that have been added in the session. They are shown at the left panel of the GUI. + +=== Upload Resume +. Prerequisites: There is an interviewee named Bob, with alias Bobby and id 1, and his resume located at `path`. +. Test case: `upload 5` + +Expected: An error message will be shown, stating that there is no interviewee that haa `5` as an identifier. +. Test case: `upload 1` + +Expected: A window will pop up, that allows to choose a file that serves as Bob's resume. +. Test case: `upload 1 -p unidentifiedpath` + +Expected: An error message will be shown, because there is no file at the given path. +. Test case: `upload 1 -p path` + +Expected: The file at `path` will be served as the resume of Bob. + +=== Open Resume +. Prerequisites: There are two interviewees, one has an alias Alice, and a resume has been uploaded under her name, and +the other is named Bob, and there is no resume under him yet. +. Test case: `resume Alice` + +Expected: A window pops up, showing the resume of Alice. +. Test case: `resume Bob` + +Expected: An error message is shown because there is no resume uploaded for Bob yet. + +=== Interviewing +. Prerequisites: The session has been finalized and there is an interviewee named Jane. +.. Test case: `interview Bob` + +Expected: An error message will be shown, because there is no interviewee that is identified as Bob. +.. Test case: `interview Jane` + +Expected: The GUI will switch to interview screen, and the app enters the Interview Phase. + +. Prerequisites: The app is in interview mode with an interviewee named Jane, has her resume uploaded and there is an +attribute named leadership, and a question with a description `how do you do?`. +.. Test case: `this girl is very good` + +Expected: The sentence will be added as a remark. The GUI will show the remark with the corresponding timestamp. +.. Test case: `:set agility 4` + +Expected: An error message will be shown since there is no attribute with name `agility`. +.. Test case: `:set leadership 11` + +Expected: An error message will be shown since the score that can be added is between 0 to 10 inclusive. +.. Test case: `:set lea 6` + +Expected: The score of attribute `leadership` will be set to 6 since `lea` is a unique prefix for leadership. +.. Test case: `start q10` + +Expected: An error message will be shown since there is no question with number 10. +.. Test case: `start q1` + +Expected: The GUI will show the starting point of the question 1. +.. Test case: `:attributes` + +Expected: The right panel of the GUI will show all the attributes that have been added, which in this case shows one +attribtue, which is leadership. +.. Test case: `:metrics` + +Expected: The right panel of the GUI will show all the metrics that have been added, which in this case is empty. +.. Test case: `:questions` + +Expected: The right panel of the GUI will show all the questions that have been added, which in this case, is a question +`how do you do?`. +.. Test case: `:resume` + +Expected: A window will pop up, showing the resume of Jane. +.. Test case: `:end` + +Expected: An error message will be shown since there is an attribute that has not been scored. +.. Test case: `:set lea 6` then `:end` + +Expected: First, the score of attribute `leadership` will be set to 6. Then, the interview is ended and the GUI shows +the normal screen. + +=== Dealing with Interviewee's Report +. Prerequisites: There are two interviewees, Alice who has been interviewed, and Bob who has not been interviewed yet. +.. Test case: `open Bob` + +Expected: An error message will be shown since Bob has not been interviewed. +.. Test case: `close report` + +Expected: An error message will be shown since currently the app is not opening any report. +.. Test case: `open Alice` + +Expected: The GUI will show the report of Alice, which is identical to the interview screen, showing the remarks that have been +added, as well as the scores that have been added. +.. Test case: `open Alice` then `close report` + +Expected: The GUI will show the report of Alice, similar to the previous case. Then, after executing the `close report` command, +the GUI will switch back to the normal screen. +.. Test case: `report Bob` + +Expected: An error message will be shown, because Bob has not been interviewed yet. +.. Test case: `report Alice` + +Expected: A success message will be shown, specifying the directory where the PDF file is exported. The PDF file can be found +at the specified directory. +. Prerequisites: The app is opening Alice's report. The report has several remarks, and there are 2 questions, the first +question was asked during the interview, and the second one was not. The interview is done within 10 minutes. +.. Test case: `goto q100` + +Expected: An error message will be shown, indicating that there are only 2 questions in the interview session. +.. Test case: `goto q2` + +Expected: An error message will be shown, indicating that the second question was not answered during the interview. +.. Test case: `goto q1` + +Expected: The remark panel of the GUI will navigate to the first remark after the first question is started. +.. Test case: `goto 0.00` + +Expected: The remark panel of the GUI will navigate to the very first remark. +.. Test case: `goto 5.00` + +Expected: The remark panel of the GUI will navigate to the first remark after the fifth minute. +.. Test case: `goto 15.00` + +Expected: The remark panel of the GUI will navigate to the very last remark. + +=== Create, Edit, Delete, and List Metrics +. Prerequisites: The app has been finalized, with three attributes: `one`, `two`, and `three`. +.. Test case: `add metric extreme` + +Expected: An error message will be shown, indicating that it misses the attribute and weight details. +.. Test case: `add metric extreme -a four -w 0.5` + +Expected: An error message will be shown, indicating that there is no attribute `four`. +.. Test case: `add metric extreme one -a two` + +Expected: An error message will be shown, indicating that it misses the attribute and weight details because there is no w +weight assigned to attribute `two`. +.. Test case: `add metric extreme one -a two -w 0.4 -a on -w 0.6` + +Expected: A metric with name `extreme` will be created with weight `0.6` assigned to `one` since `on` is a unique prefix for +`one`, and `0.4` assigned to `two`. + +. Prerequisites: Same as point 1, with an addition of existing metric named `extreme` with the same attribute to weight +as case 1d. +.. Test case: `edit metric extreme` + +Expected: An error message will be shown, indicating that it misses the attribute and weight details. +.. Test case: `edit metric extreme -n soft` + +Expected: The metric name is changed from `extreme` to `soft`. +.. Test case: `edit metric extreme -a two` + +Expected: An error message will be shown, indicating that it misses the attribute and weight details because there is no w +weight assigned to attribute `two`. +.. Test case: `edit metric extreme -a two -w 0.9` + +Expected: The weight of attribute `two` in metric `extreme` will be changed to `0.9`. +.. Test case: `edit metric extreme -a thr -w 0.5` + +Expected: It introduces the weight for attribute `three` to be assigned to weight `0.5` to metric `extreme`. The rest remains +unchanged. +.. Test case: `delete metric dictator` + +Expected: An error message will be shown, indicating that there is no metric `dictator`. +.. Test case: `delete metric extr` + +Expected: The metric `extreme` is being deleted because `extr` is a unique prefix for metric `extreme`. +.. Test case: `metrics` + +Expected: The right panel of thee GUI will show all the metrics that have been created. In this case, the metric `extreme`. + +=== Find Best +. Prerequisites: The session has been finalized, with the following interviewed interviewees details: + +Name: Alice + +Attributes to score: [one: 5, two: 4, three: 3] + +Name: Bob + +Attributes to score: [one: 2, two: 4, three: 5] + +Name: Charlie + +Attributes to score: [one: 4, two: 7, three: 4] +.. Test case: `interviewee -best` + +Expected: An error message is shown, showing that the command format is invalid. +.. Test case: `interviewee -best 5` + +Expected: All 3 interviewed interviewees are listed in sorted order based on their overall score (Charlie, Alice, Bob). +The output message indicates that there are only 3 interviewed interviewees. +.. Test case: `interviewee -best 2 -a tw` + +Expected: All 3 interviewed interviewees are listed in sorted order based on their score for attribute `two` (Charlie, Alice, Bob). +Both Alice and Bob are shown because they have the same score. The output message also indicates that there are ties, which leads +to show more than 2 interviewees. +. Prerequisites: In addition to the prerequisites stated in point 1, there is a metric named `extreme` with the following +weightage: [one: 0.4, two: 0.3] +.. Test case: `interviewee -best 2 -m extr` + +Expected: Charlie and Alice will be shown in the best interviewees list. diff --git a/docs/LearningOutcomes.adoc b/docs/LearningOutcomes.adoc deleted file mode 100644 index 436c1777617..00000000000 --- a/docs/LearningOutcomes.adoc +++ /dev/null @@ -1,216 +0,0 @@ -= Learning Outcomes -:site-section: LearningOutcomes -:toc: macro -:toc-title: -:toclevels: 1 -:imagesDir: images -:stylesDir: stylesheets -ifdef::env-github[] -:note-caption: :information_source: -endif::[] - -After studying this code and completing the corresponding exercises, you should be able to, - -toc::[] - -== Utilize User Stories `[LO-UserStories]` - -=== References - -* https://se-edu.github.io/se-book/specifyingRequirements/userStories/[se-edu/se-book: Requirements: Specifying Requirements: User Stories] - -=== Exercise: Add more user stories - -* Assume you are planing to expand the functionality of the AddressBook (but keep it as a CLI application). -What other user stories do you think AddressBook should support? Add those user stories to the `DeveloperGuide.adoc`. - -== Utilize use cases `[LO-UseCases]` - -=== References - -* https://se-edu.github.io/se-book/specifyingRequirements/useCases/[se-edu/se-book: Requirements: Specifying Requirements: Use Cases] - -=== Exercise: Add a 'Rename tag' use case - -* Add a use case to the `DeveloperGuide.adoc` to cover the case of _renaming of an existing tag_. -e.g. rename the tag `friends` to `buddies` (i.e. all persons who had the `friends` tag will now have -a `buddies` tag instead) -Assume that AddressBook confirms the change with the user before carrying out the operation. - -== Use Non Functional Requirements `[LO-NFR]` - -=== References - -* https://se-edu.github.io/se-book/requirements/nonFunctionalRequirements/[se-edu/se-book: Requirements: Non-Functional Requirements] - -=== Exercise: Add more NFRs - -* Add some more NFRs to the `DeveloperGuide.adoc` - -== Use Polymorphism `[LO-Polymorphism]` - -Note how the `Command::execute()` method shows polymorphic behavior. - -=== References - -* https://se-edu.github.io/se-book/oop/polymorphism/[se-edu/se-book: Paradigms: OOP: Polymorphism] -* https://se-edu.github.io/se-book/cppToJava/inheritance/polymorphism/[se-edu/se-book: C++ to Java: OOP: Polymorphism] - -=== Exercise: Add a polymorphic `isMutating` method - -* Add a method `boolean isMutating()` to the `Command` class. This method will return `true` for -command types that mutate the data. e.g. `AddCommand` -* Currently, AddressBook data are saved to the file after every command. -Take advantage of the the new method you added to limit file saving to only for command types that mutate data. -i.e. `add` command should always save the data while `list` command should never save data to the file. - -[NOTE] -==== -There may be better ways to limit file saving to commands that mutate data. The above approach, while not -optimal, will give you chance to implement a polymorphic behavior. -==== - -== Use abstract classes/methods `[LO-Abstract]` - -=== References - -* https://se-edu.github.io/se-book/oop/inheritance/abstractClasses/[se-edu/se-book: Paradigms: OOP: Abstract Classes] -* https://se-edu.github.io/se-book/cppToJava/inheritance/abstractClassesAndMethods/[se-edu/se-book: C++ to Java: OOP: Abstract Classes] - -=== Exercise: Make `Command#execute()` method abstract - -* Make the `Command#execute()` method abstract (hint: refer to the comment given below the method) - -== Use interfaces `[LO-Interfaces]` - -Note how the `AddressBook` class implements the `ReadOnlyAddressBook` interface so that clients who don't need write access to the `AddressBook` can access the `AddressBook` through the `ReadOnlyAddressBook` interface instead. - -image::ReadOnlyAddressBookUsage.png[width=500] - -=== References - -* https://se-edu.github.io/se-book/oop/inheritance/interfaces/[se-edu/se-book: Paradigms: OOP: Abstract Interfaces] -* https://se-edu.github.io/se-book/cppToJava/inheritance/interfaces/[se-edu/se-book: C++ to Java: OOP: Abstract Interfaces] - -=== Exercise: Add a `Printable` interface - -* Add a `Printable` interface as follows. -+ -image::PrintableInterface.png[width=400] -* `Override` the `getPrintableString` in classes `Name`, `Phone`, `Email`, and `Address` so that each produces a printable string representation of the object. e.g. `Name: John Smith`, `Phone: 12349862` -* Add the following method in a suitable place of some other class. Note how the method depends on the Interface. -+ -[source,java] ----- -/** - * Returns a concatenated version of the printable strings of each object. - */ -String getPrintableString(Printable... printables) { ----- -+ -The above method can be used to get a printable string representing a bunch of person details. -For example, you should be able to call that method like this: -+ -[source,java] ----- -// p is a Person object -return getPrintableString(p.getPhone(), p.getEmail(), p.getAddress()); ----- - -== Follow Liskov Substitution Principle `[LO-LSP]` - -=== References - -* https://se-edu.github.io/se-book/principles/liskovSubstitutionPrinciple/[se-edu/se-book: Principles: Liskov Substitution Principle] - -=== Exercise: Add an exception to an overridden method - -* Add a `throws Exception` clause to the `AddCommand::execute` method. Notice how Java compiler will not allow it, -unless you add the same `throws` clause to the parent class method. This is because if a child class throws -an exception that is not specified by the Parent's contract, the child class is no longer substitutable in place of -the parent class. -* Also note that while in the above example the compiler enforces LSP, there are other situations where it is up to -the programmer to enforce it. For example, if the method in the parent class works for `null` input, the overridden -method in the child class should not reject `null` inputs. This will not be enforced by the compiler. - -== Use Java-FX for GUI programming `[LO-JavaFx]` - -=== References - -* https://se-edu.github.io/se-book/javaTools/javaFXBasic/[se-edu/se-book: Tools: Java: JavaFX: Basic] - -=== Exercise: Enhance GUI - -* Do some enhancements to the AddressBook GUI. e.g. add an application icon, change font size/style - -== Analyze Coupling and Cohesion of designs `[LO-CouplingCohesion]` - -* Notice how having a separate `ParserUtil` class to handle user input validation, space trimming etc. of model data (an application of the Single Responsibility Principle) improves the _cohesion_ of the model component (since it does not need to be concerned with handling user input) as well as the `ParserUtil` class. - -=== References - -* https://se-edu.github.io/se-book/designFundamentals/coupling/[se-edu/se-book: Design: Design Principles: Coupling] -* https://se-edu.github.io/se-book/designFundamentals/cohesion/[se-edu/se-book: Design: Design Principles: Cohesion] - -=== Exercise: Identify places to reduce coupling and increase cohesion - -* Where else in the design coupling can be reduced further, or cohesion can be increased further? - -[[apply-dependency-inversion-principle-lo-dip]] -== Apply Dependency Inversion Principle `[LO-DIP]` - -* Note how the `LogicManager` class doesn't depend on `StorageManager` directly, but rather the interface `Storage`. -This is an application of the Dependency Inversion Principle. -+ -image::LogicStorageDIP.png[width=300] -* Where else in the code do you notice the application of DIP? - -=== References - -* https://se-edu.github.io/se-book/principles/dependencyInversionPrinciple/[se-edu/se-book: Principles: Dependency Inversion Principle] - -== Use Dependency Injection `[LO-DI]` - -Notice how the `LogicManager` class does not depend on the `StorageManager` class, but depends on the `Storage` interface. -This allows us to use _Dependency Injection_ to test the `LogicManager` class without getting the `StorageManager` class involved. - -=== References - -* https://se-edu.github.io/se-book/testing/dependencyInjection/[se-edu/se-book: Quality Assurance: Testing: Dependency Injection] - -=== Exercise: Facilitate injecting a StorageStub - -* Notice how `LogicManagerTest` tests `LogicManager` by constructing a `StorageManager` object. -* Implement `StorageStub` such that calls to its `save*` methods do nothing (i.e. empty method body). -* Update `LogicManagerTest` to work with the `StorageStub` instead of the actual `StorageManager` object. -i.e. `LogicManagerTest` injects a `StorageStub` object when constructing a `LogicManager` before testing it. -+ -image::DependencyInjection.png[width=600] -* The example above uses <> as a means to achieve DI. -Note that there is another way to inject a `StorageStub` object, as shown below. -In this case we do not apply the DIP but we still achieve DI. -+ -image::DependencyInjectionWithoutDIP.png[width=250] - -== Apply Open-Closed Principle `[LO-OCP]` - -=== References - -* https://se-edu.github.io/se-book/principles/openClosedPrinciple/[se-edu/se-book: Principles: Open-Closed Principle] - -=== Exercise: Analyze OCP-compliance of the `LogicManager` class - -* Consider adding a new command to the Address Book. e.g. an `edit` command. Notice how little you need to change in the `LogicManager` class to extend its behavior so that it can execute the new command. -That is because `LogicManager` follows the OCP i.e. `LogicManager` is _open to be extended_ with more commands but _closed for modifications_. -* Is it possible to make the `AddressBookParser` class more OCP-compliant in terms of extending it to handle more -command types? -* In terms of how it saves data, is `LogicManager` more OCP-compliant -due to the application of DIP as given in <>? -How can you improve ``LogicManager``'s OCP-compliance further so that it can not only work with different types -of storages, but different number of storages (e.g. save to both a text file and a database). - -== Work in a 3KLoC code base `[LO-3KLoC]` - -=== Exercise: Enhance AddressBook - -* Enhance AddressBook in some way. e.g. add a new command diff --git a/docs/SettingUp.adoc b/docs/SettingUp.adoc index c0659782fab..90a51488f39 100644 --- a/docs/SettingUp.adoc +++ b/docs/SettingUp.adoc @@ -37,7 +37,7 @@ Do not disable them. If you have disabled them, go to `File` > `Settings` > `Plu == Verifying the setup -. Run the `seedu.address.Main` and try a few commands +. Run the `Main` and try a few commands . <> to ensure they all pass. == Configurations to do before writing code diff --git a/docs/Testing.adoc b/docs/Testing.adoc index 5767b92912c..a36c345d55e 100644 --- a/docs/Testing.adoc +++ b/docs/Testing.adoc @@ -37,9 +37,9 @@ We have three types of tests: . _Unit tests_ targeting the lowest level methods/classes. + e.g. `seedu.address.commons.StringUtilTest` . _Integration tests_ that are checking the integration of multiple code units (those code units are assumed to be working). + -e.g. `seedu.address.storage.StorageManagerTest` +e.g. `StorageManagerTest` . Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together. + -e.g. `seedu.address.logic.LogicManagerTest` +e.g. `LogicManagerTest` == Troubleshooting Testing diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 4e5d297a19f..30ac67e935b 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - User Guide += HireLah! - User Guide :site-section: UserGuide :toc: :toc-title: @@ -12,166 +12,1198 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3 +:repoURL: https://github.com/AY1920S2-CS2103-W15-2/main -By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` +By: `AY1920S2-W15-2` Since: `Feb 2020` + +// tag::intro[] == Introduction -AddressBook Level 3 (AB3) is for those who *prefer to use a desktop app for managing contacts*. More importantly, AB3 is *optimized for those who prefer to work with a Command Line Interface* (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. Interested? Jump to the <> to get started. Enjoy! +*HireLah!* is a productivity app that helps busy interviewers like you manage multiple interview sessions in an orderly +manner. During each interview session, *HireLah!* helps you to keep track of interviewee information and interview +rubrics in one central location so that you can focus on what's important - the interview itself! +{empty} + +{empty} + -== Quick Start +*HireLah!* has the following main features: + +. Manage multiple interview sessions, one for each different job position you are conducting interview for. +. Add and modify interview rubrics for each interview session, including: +.. A list of attributes you want to look out for in an interviewee. +.. A set of questions that you would like to ask all interviewees. +.. A collection of metrics to score interviewees, allowing you to easily rank potential candidates. +. Record your remarks for an interviewee during an interview, and score the interviewee based on the attributes. +. Identify the best interviewees based on particular attributes or metrics, so that you can follow up with them. +. Export interview transcripts for future reference. +{empty} + +{empty} + + +*HireLah!* is optimised for interviewers who are fast typists and prefer typing over other means of input. It comes with: + +* A Command Line Interface (CLI) which allows you to access all *HireLah!* features by typing. +* A Graphical User Interface (GUI) that displays the information you need to conduct an interview, with limited GUI +convenience features. + +== About this Guide -. Ensure you have Java `11` or above installed in your Computer. -. Download the latest `addressbook.jar` link:{repoURL}/releases[here]. -. Copy the file to the folder you want to use as the home folder for your Address Book. -. Double-click the file to start the app. The GUI should appear in a few seconds. +=== Basic Information +This User Guide explains how you can use *HireLah!* to help you manage your interviews. + +You may refer to <> for a short tutorial on how to run *HireLah!* on your device and use *HireLah!*'s main +features. Please refer to <> for a breakdown of *HireLah!*'s GUI components. For a full walkthrough of +*HireLah!*, please refer to <>. + +If you encounter any difficulties while using *HireLah!*, you can refer to <> for a list of common issues. + +If you are an experienced CLI user, you can find all the *HireLah!* commands under <>. + +=== Command Format +This section explains the format of commands in this User Guide: +==== +*Command Format* + +* Words in `` are the parameters to be supplied by the user e.g. in `add interviewee `, `` is a parameter which can be used as `add interviewee John Doe`. +* Items in square brackets are optional e.g ` [-aka ]` can be used as `John Doe -aka Jo` or as `John Doe`. +* Items with `…`​ after them can be used multiple times including zero times, unless otherwise stated e.g. `[-a -w ]...` can be used as, `-a ambition -w 0.5`, `-a ambition -w 0.5 -a teamwork -w 0.1` etc. +==== + +== Quick Start +. Ensure you have Java 11 or above installed in your Computer. +. Download the latest `HireLah.jar` link:{repoURL}/releases[here]. +. Copy the file to the folder you want to use as the home folder for your HireLah!. +. Double-click the file to start the app. The following window should appear within a few seconds - this is the Session +Screen, where you can create, open, or delete interview sessions: + -image::Ui.png[width="790"] +.Image of the Session Screen +image::Screenshots/EmptySessionScreen.png[width="790"] + . Type the command in the command box and press kbd:[Enter] to execute it. + -e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. +e.g. typing *`help`* and pressing kbd:[Enter] will open this user guide. . Some example commands you can try: +* *`new`* `CEO Interview`: creates an interview session named "CEO Interview" and opens the Interview Screen. ++ +.Image of the Interview Screen +image::Screenshots/EmptyInterviewScreen.png[width="790"] ++ +. Once you are on the Interview Screen shown above, here are some example commands you can try: +* *`add interviewee`* `John Doe`: adds an interviewee named John Doe to the application +* *`add question`* `How old are you?`: adds the question to the application +* *`exit`*: exits the app +. Refer to <> for an explanation of the GUI and <> for details of all the commands. + +== GUI Components +*HireLah!* consists of two main screens, the Session Screen as well as the Interview Screen, as shown below. + +=== Session Screen +.Breakdown of the Session Screen +image::Screenshots/GUIComponent1.png[width="790"] + +==== Menu Bar +You may use the Menu Bar to access this User Guide, or to exit *HireLah!*. -* *`list`* : lists all contacts -* **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : adds a contact named `John Doe` to the Address Book. -* **`delete`**`3` : deletes the 3rd contact shown in the current list -* *`exit`* : exits the app +==== Session List Display +Displays a list of all current interview sessions that has been created using *HireLah!* -. Refer to <> for details of each command. +For more information, see <>. -[[Features]] +==== Result Display Box +The Result Display Box displays success and error messages for your last command. + +==== Command Box +This is where you can type commands into the CLI to interact with this app. + +{empty} + + +=== Interview Screen +.Breakdown of the Interview Screen +image::Screenshots/GUIComponent2.png[width="790"] + +==== Menu Bar +Refer to <> + +==== Session Info Display +This panel displays a few meta information about this interview session, in order: + +* The name given to this interview session. +* The number of interviewees that has been interviewed over the total number of interviewees. +* Whether this session has been finalised. Refer to <> for more information. + +==== Interviewee List Display +Interviewee List Display shows the current list of interviewees. This list will display either the list of all +interviewees, or a list of the best few interviewees selected based on your input ranking criteria, as shown below: (See <>) + +.*LEFT* List of all interviewees, *RIGHT* List of best interviewees. +image::Screenshots/IntervieweeVSBestInterviewee.png[width="500"] + +Each interviewee card contains the full name, ID and alias of the interviewee. It also shows whether the +interviewee has had uploaded a resume (indicated by a green circle with a tick beside "Resume:") and the interview +status of the interviewee (white circle indicates the interviewee has not been interviewed, yellow circle indicates +that interview with this interviewee is currently underway/incomplete, green circle indicates that the interview with +this interviewee has been completed). + +In addition, if the list of best interviewees is being shown, the score of each interviewee based on the ranking +criteria will be displayed on each interviewee's card. + +==== Transcript Panel +Transcript Panel displays the interview transcript of an interviewee. It consists of two sub-components: + +* A Detailed Interviewee Card on the left, which contains: + . Full name, ID and alias of the interviewee. + . A button that opens the interviewee's resume. + . A table showing the score assigned to the interviewee for each attribute that this interview looks out for. If an +attribute has not been scored yet, the score will be shown as "-". + +* A Remarks List showing the time-stamped remarks entered by the user about the interviewee during the interview. The +list also contains question headers, indicating that the remarks entered after it are in response to the interviewee's +answer to the particular question. + +==== Rubrics Panel +Rubrics Panel displays information about attributes, questions and metrics created for this interview session. You can +easily toggle between these lists of information by clicking on the respective tab headers at the top of this Panel. + +==== Result Display Box +Refer to <> + +==== Command Box +Refer to <> + +// end::intro[] + +// tag::FeaturesIntro[] == Features -==== -*Command Format* +Generally, in an interview session (for example, an interview for a role in a company), there are 3 phases: -* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. -* Items in square brackets are optional e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. -* Items with `…`​ after them can be used multiple times including zero times e.g. `[t/TAG]...` can be used as `{nbsp}` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. -* Parameters can be in any order e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. -==== +. Initialization phase (or preparation phase) - where the interviewer prepares his/her questions, manages + candidates, receives their resumes, etc. +. Interview phase - where the interviewer actually conducts interviews, asking questions and + making notes about each candidate +. Decision phase - after interviewing all candidates, the interviewer summarises his/her opinions, refers + to the remarks he/she made of each interviewee and decides who to select -=== Viewing help : `help` +These 3 phases are clearly defined in *HireLah!*. What you can do in each phase is listed below. -Format: `help` +=== Viewing help -=== Adding a person: `add` +*Command* -Adds a person to the address book + -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +You never know when you need to refer to this guide. For that reason, the helpful `help` command is available +always within the app, except during an interview, where "help" may be a remark you want to make about the current +candidate being interviewed! Still, the help command is always accessible via the kbd:[F1] key, or by clicking the +"Help" button on the top of the menu. + +.... +help +.... + +_Action:_ This user guide is opened as a PDF with your system's default PDF viewer. + +=== Exiting the App + +Similar to help, `exit` is always available to close the app, except during an interview (Why would you want +to close the app during an interview?). + +*Command* + +.... +exit +.... + +_Action:_ The app exits. +// end::FeaturesIntro[] + + +// tag::session[] +=== Managing Sessions + +*HireLah!* allows you to clearly separate your interview sessions. After all, it would be disastrous to mix up your candidates +for one job with the candidates for a totally different role, or ask the wrong questions to the wrong person! + +==== New Session + +When you first start the app, there will be no existing sessions. You can create a new session to get started via the `new` command. [TIP] -A person can have any number of tags (including 0) +Sessions are stored as directories in a `/data` directory HireLah! creates in the same location as the app. To make sure +session names are valid, we only allow Alphanumeric characters (a, b, c or 1, 2, 3) and spaces (but not all spaces!). + +Do not accidentally delete this folder! *HireLah!* will not be able to restore your data if you delete it! + +*Command* + +.... +new +.... + +*Execution Example* + +.... +> new ceo interview +.... + +_Action:_ Creates a new session called "ceo interview" and opens the session. + +image::Screenshots/NewSession.png[] + +==== Open and Close Sessions + +On subsequent uses of *HireLah!*, you will be greeted at the starting screen with all the sessions you have +created. You can choose to start a new session again, or open a previous session to continue working in it using the +`open` command. After you are done for the day, or need to switch to another session, simply close the session with the +`close session` command. Closing a session will naturally return you to the starting screen where you can open or create new +sessions. + +*Commands* + +.... +open +close session +.... + +==== Delete Session + +After a long time, old sessions may not be relevant anymore. To clear space for new interviews, use the `delete` command. + +*Command* + +.... +delete +.... + +*Execution Example* + +.... +> delete ceo interview +.... + +_Action:_ Deletes the ceo interview session and all its data. + +_Output:_ + +.... +Successfully deleted the session: ceo interview +.... +// end::session[] + +=== Initialization Phase + +// tag::interviewees[] +==== Add, Edit and Delete Interviewees + +*HireLah!* allows you to manage your list of candidates from within the app. You can add interviewees to the list +by giving their full name, and *HireLah!* automatically generates a unique ID for each candidate. To make things easier, +you can also specify an alias (a shorter name) to the interviewee, so you need not memorise their IDs or type out their +full names every time. + +Whenever you see ``, it means that you can specify the interviewee you are referring to via any of his/her +unique identifiers - ID, full name or alias. + +At any time you can delete any interviewees and edit their information. + +[TIP] +Most names and aliases are accepted - even non-English names or names with funny punctuation and numbers (eg. Larry O'Brien the 3rd). +Your interviewees could come from anywhere after all. But do not give blanks or a name full of numbers (eg. 12345)! + +[TIP] +If you are adding an interviewee with an alias, but you accidentally give an invalid alias (the same as another interviewee's name or alias) +*HireLah!* does what it can - it adds the interviewee, but tells you that it failed to add the alias. + +If you are editing an interviewee and specify both a new name and new alias, if either are invalid, *HireLah!* will prevent you from performing that action! + +*Commands* + +.... +add interviewee [-aka ] +edit interviewee [-n ] [-aka ] +delete interviewee +.... + +*Execution Example* + +.... +> add interviewee Jane Doe -aka Doe +> edit interviewee Jane Doe -n Janice Doe -aka JDoe +> delete interviewee Janice Doe +.... + +_Action:_ Interviewee Jane Doe with alias of Doe is first created and +then her name is changed to Janice Doe. + +_Output:_ + +.... +New interviewee added: Jane Doe + +Edited interviewee: Jane Doe + +Deleted interviewee: Janice Doe +.... + +image::Screenshots/Interviewee.png[] +{empty} + + +==== Upload Resume + +Interviewer can upload a resume for a specific interviewee. If the path is specified, +the resume will be uploaded from the specified path. Otherwise, it will open a file-picker. + +[TIP] +*HireLah!* does *NOT* copy the file you upload! It merely remembers the location of the resume. +If you delete or move the file, *HireLah!* can no longer open the resume! + +We recommend that you organize all the resumes in a folder, perhaps next to the `/data` directory. + +*Command* + +.... +upload [-p ] +.... + +*Execution Example* + +.... +> upload Jane Doe +.... + +_Action:_ Resume is selected by the user via a file-picker window and then uploaded for the interviewee Jane Doe. + +_Output after selecting the resume file:_ + +.... +Successfully added the resume! +.... + +image::Screenshots/Upload.png[] +{empty} + + +==== Open Resume + +Interviewer can open a resume from a specific interviewee. + +[WARNING] +Note: Make sure you have a default app for viewing PDF files, or whatever format your resumes are in! +*HireLah!* tries to open the file you give as a resume, but if your system does not know how to open the file, +*HireLah!* will not be able to help! As far as *HireLah!* is concerned, it has successfully performed its job +to request your system to open the file. + +*Command* + +.... +resume Jane Doe +.... + +_Action:_ Jane Doe's resume is opened. + +_Output:_ + +.... +Resume of Jane Doe opened. +.... +{empty} + +// end::interviewees[] + +[[Attributes]] +// tag::attributes[] +==== Add, Edit and Delete Attributes + +In an interview, you often look out for a particular set of attributes in your candidates. +*HireLah!* lets you keep track of what to look out for during the interview, and even assign +scores for each attribute to the interviewees so you can compare them after interviewing. + +[TIP] +An attribute's name can only contain alphabets and spaces, and it should not be blank! + +*Command* + +.... +> add attribute +> edit attribute -a +> delete attribute +.... + +*Execution Example* + +.... +> add attribute teamwork +> add attribute leadersip +> edit attribute leadersip -a leadership +> delete attribute teamwork +.... + +_Action:_ teamwork and leadersip is first added. leadersip (with a typo) is edited +to leadership, and then teamwork is deleted. + +_Output:_ + +.... +New attribute added: teamwork + +New attribute added: leadersip + +Edited attribute: leadersip to leadership + +Deleted attribute: teamwork +.... + +image::Screenshots/Attribute.png[] +{empty} + +// end::attributes[] + +// tag::questions[] +==== Add, Edit and Delete Interview Questions + +Before the interview process, the interviewer may wish to review the +questions. During review process before the commencement of interviews, the interviewer may want to add more +questions, remove unnecessary questions or modify existing questions. + +*Command* + +.... +add question +edit question -q +delete question +.... + +*Example* + +.... +> add question What are your relevant experiences? +> add question What are your strengths? +> edit question 1 -q What do you hope to accomplish in 5 years? +> edit question 2 -q How can you add value to the company? +> delete question 1 +.... + +_Action:_ The question "What are your relevant experiences?" will be added first, followed by the question "What are +your strengths?". Then, question 1 and 2 are edited, before question 1 is deleted. + +_Output:_ + +.... +New question added: What are your relevant experiences? + +New question added: What are your strengths? + +Edited question 1 to What do you hope to accomplish in 5 years? + +Edited question 2 to How can you add value to the company? + +Deleted question: What do you hope to accomplish in 5 years? +.... + +image::Screenshots/Question.png[] +{empty} + + +// end::questions[] + +//tag::load[] +==== Load Attributes and Questions from other Session +In case of having multiple sessions with the same attributes and questions. There is a `load` command +to load attributes and questions from other session. Therefore, there is no extra work to do. + +*Command* + +.... +load attributes +load questions +.... + +*Example* + +.... +> load attributes ceo +> load questions janitor +.... + +_Output:_ + +.... +Loaded attributes from ceo + +Loaded questions from janitor +.... + +image::Screenshots/Load.png[] +{empty} + +// end::load[] + +// tag::view[] +==== Switching views + +When working on the attributes, questions and metrics (more about metrics in <>), +*HireLah!* displays the relevant content you are working on on the right side of the application. +To switch views between them, *HireLah!* also provides these commands. + +*Command* + +.... +attributes +questions +metrics +.... + +_Action:_ Switches between displaying attributes, questions and metrics. + +_Output:_ + +.... +Here is the list of attributes: + +Here is the list of questions: + +Here is the list of metrics: +.... +{empty} + +{empty} + +On the left side of the application, all of the interviewees are listed. It will switch to only show several +best interviewees when the the command is entered by the user. To switch back to list down the interviewees, HireLah! +provides this command. + +*Command* + +.... +interviewees +.... + +_Action:_ Displays the interviewee list. + +_Output:_ + +.... +Here is the list of interviewees: +.... +{empty} + -Examples: +// end::view[] -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +// tag::finalise[] +==== Finalise Interview Attributes and Questions -=== Listing all persons : `list` +After the interviewer is satisfied with the list of questions and attributes created for an interview session, the +interviewer must finalise changes in attributes and questions. This is to ensure that the interviewers will evaluate all +interviewees fairly and equally using the same set of questions and attributes. -Shows a list of all persons in the address book. + -Format: `list` +[WARNING] +Finalise is *irreversible*! Make sure you are sure of the attributes and questions you have, since *HireLah!* will +not allow you to make changes to them after giving this command. This may seem restrictive, but this consistency will +give you guarantees that make *HireLah!* really useful. For example, later on, if you open someone's interview report and +find his/her answer to a question, then go to another report and view the same question, you are guaranteed that both interviewees +were asked the same thing! -=== Editing a person : `edit` +*Command* -Edits an existing person in the address book. + -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...` +.... +finalise +.... -**** -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index *must be a positive integer* 1, 2, 3, ... -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person's tags by typing `t/` without specifying any tags after it. -**** +_Output:_ -Examples: +.... +Attributes and questions of this interview session has been finalised. You cannot change them anymore. +.... -* `edit 1 p/91234567 e/johndoe@example.com` + -Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` + -Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +image::Screenshots/Finalise.png[] +{empty} + -=== Locating persons by name: `find` +// end::finalise[] -Finds persons whose names contain any of the given keywords. + -Format: `find KEYWORD [MORE_KEYWORDS]` +// tag::interviewPhase[] -**** -* The search is case insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` -**** +=== Interview Phase -Examples: +==== Start Interview -* `find John` + -Returns `john` and `John Doe` -* `find Betsy Tim John` + -Returns any person having names `Betsy`, `Tim`, or `John` +After finalising, you can start to interview the interviewees. -// tag::delete[] -=== Deleting a person : `delete` +*Command* -Deletes the specified person from the address book. + -Format: `delete INDEX` +.... +interview +.... -**** -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index *must be a positive integer* 1, 2, 3, ... -**** +*Example* -Examples: +.... +> interview Jane Doe +.... -* `list` + -`delete 2` + -Deletes the 2nd person in the address book. -* `find Betsy` + -`delete 1` + -Deletes the 1st person in the results of the `find` command. +_Action:_ Initializes an interview session with Jane. The app +also starts the interview time. -// end::delete[] -=== Clearing all entries : `clear` +_Output:_ -Clears all entries from the address book. + -Format: `clear` +.... +Interview with Jane Doe started! +.... -=== Exiting the program : `exit` +image::Screenshots/Interview.png[] +{empty} + -Exits the program. + -Format: `exit` +==== Record Remarks -=== Saving the data +After starting the interview, *HireLah!* allows you to easily take notes within the app itself. +Anything you type in the Command Box, will be recorded as interview remarks, which are timestamped, allowing you to +easily navigate your interview remarks after the interview is over. -Address book data are saved in the hard disk automatically after any command that changes the data. + -There is no need to save manually. +*Command* -// tag::dataencryption[] -=== Encrypting data files `[coming in v2.0]` +.... + +.... -_{explain how the user can enable/disable data encryption}_ -// end::dataencryption[] +*Example* + +.... +> Interesting family history, all of her family members are very rich. +.... + +_Action:_ Stores this remark and maps it to the specific time when you +enter it. + +_Output:_ + +.... +Added the remark. +.... + +image::Screenshots/Remark.png[] +{empty} + + +==== Asking Questions + +Special commands while interviewing are prefixed with a ":". These commands are +not recorded as remarks and instead perform special actions. + +The start question command allows you to indicate when you asked a question during +the interview. This allows you to easily navigate to relevant parts of the interview +after the interview ends, while viewing the report. + +*Command* + +.... +:start q +.... + +*Example* + +.... +> :start q1 +> Strengths: resilience, ambitious, good time-management +> Weakness: perfectionist, not detail-oriented +> Short answers + +.... + +_Output:_ + +.... +Marked the start of question 1 + +Added the remark + +Added the remark + +Added the remark + +.... + +_Action:_ Marks the start of question 1, then adds some remarks related to question 1. + +image::Screenshots/StartQuestion.png[] +{empty} + + +==== Score attributes + +During the interview, as you form your opinion on the candidate, you can give them scores for the +attributes you have created earlier (see <>). + +[TIP] +To keep things sensible, HireLah! allows you to give a score from between 0 to 10 inclusive. So +9.6 and 0.2 are in, but don't try and give someone -10! + +*Command* + +.... +:set +.... + +*Example* + +.... +> :set leadership 5 +.... + +_Action:_ update the leadership score for John Doe (displayed in a table). + +_Output:_ + +.... +Scored 5.00 to leadership +.... + +image::Screenshots/AttributeScore.png[] +{empty} + + +==== Other Viewing Commands + +During the interview, only a small subset of the usual commands are available by placing ":" in front. +It would be terrible if you accidentally deleted an interviewee while interviewing him/her! However, it is +important to be able to look at the current interviewee's resume, and look at the attributes and questions +needed in the interview, thus these viewing operations are supported. + +*Command* + +.... +:resume +:questions +:attributes +:metrics +.... + +*Example* + +.... +> :resume +> :questions +.... + +_Action:_ Opens the resume of the current interviewee. Then switches the tab to show the questions. + +_Output:_ + +.... +Resume of John Doe opened. + +Here is the list of questions: +.... +{empty} + + +==== End interview + +After finishing the interview, give the end command. After the interview has ended, +you will not be allowed to make any more remarks or change the interviewee's scores. +Don't end prematurely! + +[TIP] +*HireLah!* will not allow you to end the interview if you have not assigned a score +on all the attributes! + +*Command* + +.... +:end +.... + +*Example* + +.... +> end +.... + +_Action:_ Ending the interview session. + +_Output_: + +.... +Ended interview with Jane Doe. +.... + +image::Screenshots/EndInterview.png[] +{empty} + + +=== Decision Phase + +// end::interviewPhase[] + +// tag::open[] +==== Viewing Interviewee Reports +After interviewing a candidate, you can view the interview transcript any +time by doing an open command. To close the report, you can use a close command. + +*Command +.... + +open +close report +.... + +*Example* + +.... +> open Jane Doe +.... + +_Action_: Shows the entire list of remarks made during the interview, with +their timestamps. + +_Output_: + +.... +Successfully opened Interviewee report: Jane Doe +.... + +image::Screenshots/Report.png[] +{empty} + +// end::open[] + +// tag::export[] +==== Exporting Interviewee Reports to PDF + +After interviewing a candidate, you can also export the report of the interviewee +in the form of a PDF. + +*Command* + +.... +export +.... + +*Example* + +.... +> export Jane Doe +.... + +_Action_: Exports the interview report of a particular interviewee in PDF. + +_Output_: + +.... +Report of Anna exported in /Users/jane/main with file name "2.pdf". +.... + +image::Screenshots/Export.png[] +// end::export[] + +// tag::navigation[] +==== Working in an Interviewee Report + +After opening a report, you can scroll up and down to navigate through +the remarks you made during the interview. In addition, you can zoom in +on a particular moment in the interview by question number, or by the +interview time. + +===== Navigating by Question Number + +Jumps to the point when the interviewee was +answering the given question (marked by the `:start q` command). + +*Command* + +.... +goto q +.... + +*Example* + +.... +> goto q2 +.... + +_Output_: + +.... +Here is the remark of question 2! +.... + +image::Screenshots/NavigationQuestion.png[] +{empty} + + +===== Navigating by Timestamp + +Scrolls the list of remarks to the given time. +The user can scroll up and down to see the rest of the remarks. + +*Command* + +.... +goto - + - + - + + @@ -46,14 +47,12 @@ - + - + - - - - + diff --git a/src/main/resources/view/MetricCardListView.fxml b/src/main/resources/view/MetricCardListView.fxml new file mode 100644 index 00000000000..8429084d812 --- /dev/null +++ b/src/main/resources/view/MetricCardListView.fxml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/main/resources/view/MetricListCard.fxml b/src/main/resources/view/MetricListCard.fxml new file mode 100644 index 00000000000..4ed64f2d7b9 --- /dev/null +++ b/src/main/resources/view/MetricListCard.fxml @@ -0,0 +1,28 @@ + + + + + + + + + + +
+ + + + + + + + +
+ + + + +
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f08ea32ad55..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100644 index 8836d323cc5..00000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/view/QuestionRemarkCard.fxml b/src/main/resources/view/QuestionRemarkCard.fxml new file mode 100644 index 00000000000..dd327dc374a --- /dev/null +++ b/src/main/resources/view/QuestionRemarkCard.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/main/resources/view/RemarkCard.fxml b/src/main/resources/view/RemarkCard.fxml new file mode 100644 index 00000000000..3804e38855c --- /dev/null +++ b/src/main/resources/view/RemarkCard.fxml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/main/resources/view/RemarkCardListView.fxml b/src/main/resources/view/RemarkCardListView.fxml new file mode 100644 index 00000000000..a1ca8651cb7 --- /dev/null +++ b/src/main/resources/view/RemarkCardListView.fxml @@ -0,0 +1,14 @@ + + + + + + + +
+ +
+ + + +
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..eb059588fc7 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,7 +3,7 @@ - -