iP: Week 2iP: Week 4


iP: Week 3

  1. Do any leftover iP tasks from the previous week
  2. Create a PR to the upstream repo
  3. Add Increments as branches: Level-7, Level-8
  4. Add Increments: A-MoreOOP, A-Packages, A-Gradle, A-JUnit, A-Jar
  5. Add Increments as parallel branches: A-JavaDoc, A-CodingStandard, Level-9
  6. [Optional] Get a head start on JavaFX

You are required to follow the Git standard from this week, as specified on this page.
Only future commits need to follow the Git standard. Editing past commit messages (to make them comply with the Git standard) is discouraged since it changes the commit timestamp, which can alter your iP progress timeline (as seen by progress-tracking scripts).

Reminder about the deadline for the weekly project tasks:

Repeating some FAQs from last week ...

FAQ about iP increments

FAQ How are the iP git tags used in grading?


FAQ What if I discovered a bug after I finished an increment?


FAQ I did multiple increments in the same commit. How to fix?


FAQ The requirements of an increment scheduled for this week are already satisfied by the work I did in an earlier week. What now?


FAQ My iP increments are not detected by the dashboard because I forgot to push my tags earlier. What now?


FAQ Can the iP dashboard be updated more frequently?


FAQ If I do optional increments, do I get more marks?


FAQ Oh no! I used the wrong tag/branch name.


FAQ I forked the iP repo from a previous semester. How to fix?



1 Do any leftover iP tasks from the previous week

  • Remember to do any leftover increments from the past weeks before starting on the current week's increments. This guideline applies to future weeks too.

2 Create a PR to the upstream repo

  • Create a pull request (PR) from your fork to the upstream repo. Note the following:
    • Create the PR from the master branch of your fork to the master branch of the upstream repo (https://github.com/NUS-CS2103-AY2627-S1/ip)
    • Set the PR name as [{Your full/partial name or your GitHub username}] iP
      e.g., [Richard Mathews Chee] iP or [Rich ... hee] iP or [TheRichMat] iP
      Note that the PR name will be publicly visible.
      You may leave the description empty.
      You may leave the Allow edits by maintainers box unchecked. If checked, maintainers of the upstream repo can push small fixes to your PR (for example, to resolve minor issues before merging).
    • If you created the PR correctly, it should appear in the list of PRs here.
    • Steps for creating a PR are given in Git-Mastery → Lesson: T9L1. Creating Pull Requests (omit the 'preparation' step):

There is no need to create new PRs every time you update the code in your fork. The PR will update automatically to reflect the latest code of the branch it was sent from (i.e., when you push changes to the master branch of your fork).

3 Add Increments as branches: Level-7, Level-8

%%{init: { 'theme': 'default', 'gitGraph': {'mainBranchName': 'master'}} }%%
gitGraph
commit id: "m1"
commit id: "m2"
branch branch-Level-7
checkout branch-Level-7
commit id: "b1c1"
commit id: "b1c2"
checkout master
merge branch-Level-7 id: "Merge branch ..." tag: "Level-7"
branch branch-Level-8
checkout branch-Level-8
commit id: "b2c1"
checkout master
merge branch-Level-8 id: "Merge branch-Level-8 ..." tag: "Level-8"
commit id: "m4"

If you are new to Git branching, before attempting this iP increment, first learn about it from this week's textbook topics. In particular, learn how to merge a branch without fast-forwarding.

  • Do Level 7 in a branch named branch-Level-7. Here are the steps:
    1. Start a branch branch-Level-7.
    2. Implement Level 7 while committing to that branch at appropriate points.
    3. Merge the branch back to the master branch (remember to create a merge commit, i.e., no fast-forward).
    4. Git tag the merge commit in the master branch as usual (i.e., add the tag Level-7).
    5. Push the following three things to your fork:
      (a) the master branch,
      (b) the branch-Level-7 branch,
      (c) the Level-7 tag.
      Do not delete the branch after merging. If you do, our grading scripts will not be able to detect your use of Git branches.
      Only merged branches are detected by our scripts. After merging a branch b1 into the master branch, you need to push both the master and the b1 branches to the fork. Pushing the master branch does not automatically take the b1 branch along with it just because it is already merged into the master branch.
Duke Level-7: Save

Level 7. Save

Save the tasks on the hard disk automatically whenever the task list changes. Load the data from the hard disk when the chatbot starts up. You may hard-code the file name and relative path from the project root, e.g., ./data/duke.txt

The format of the file is up to you. Example:

T | 1 | read book
D | 0 | return book | June 6th
E | 0 | project meeting | Aug 6th 2-4pm
T | 1 | join sports club

If you use file paths in your code:

  • Use relative paths rather than absolute paths such as C:\data. If not, your app can cause unpredictable results when used on another computer.
  • Specify file paths in an OS-independent way. If not, your app might not work when used on a different OS.

Your code must the case where the data file doesn't exist at the start. Reason: when someone else takes your chatbot and runs it for the first time, the required file will not exist on their computer. Similarly, if you expect the data file to be in a specific folder (e.g., ./data/), you must also handle the case where the folder doesn't exist yet.

Limit writing operations to the current folder only. The app should not create files or folders outside the folder from which it is run. In particular, do not store files in the user's home folder.

Reason: This prevents the app from inadvertently corrupting a user's files elsewhere on the disk or filling a tester's disk with files.

Stretch goal: Handle the situation of the data file being corrupted (i.e., content not in the expected format).

AI Guidance » Level 7. Save

One possible way to break this increment into small steps is:

  1. Implement writing to the file. Ask for a minimal implementation for the only.
  2. Implement reading from the file. Happy path only.
  3. Add error handling.
  4. Ask the AI to check whether the two notes above (the one about file paths and the one about the file not existing) are covered.

If you are confident that you know how to implement this increment yourself, you can ask for a more complete implementation in one go.

Here are sample prompts for the incremental approach above.

Here is the requirement:

[=====

Save the tasks on the hard disk automatically whenever the task list changes. Load the data from the hard disk when the chatbot starts up. You may hard-code the file name and relative path from the project root, e.g., ./data/duke.txt

The format of the file is up to you. Example:

T | 1 | read book
D | 0 | return book | June 6th
E | 0 | project meeting | Aug 6th 2-4pm
T | 1 | join sports club

=====]

For now, do a minimal implementation of the "happy path" for writing to the file. Reading from the file can be implemented later. Update tests and run /test-ui. At the end, run /present-changes-visually

Proceed to implementing the reading from the file, similar to the previous round.

In this round, enhance the code to handle all edge cases and possible errors.

Here are two additional requirements. Ensure the code meets them too.

...


AI Guidance » General: Compact to prevent context rot

AI tools can show how much of the context window the current chat thread has used. In Codex, typing / in an empty chat box pops up details about the ongoing chat, including a line named Compact that states the status of the context window.

It is said that the AI will start getting 'dumber' once you go past 30-40% of the context window. If that happens to you, you can use the /compact command to compact the context.

More about compacting context



FAQ Can I use other data formats (e.g., serialized objects) to save tasks?


FAQ Oops, I messed up my branching! Will I be penalized?


FAQ What if I did the increment in the master branch, not in branch-___ as required?


  • Do Level 8 in a similar way, using a branch branch-Level-8.
Duke Level-8: Dates and Times

Level 8. Dates and Times

Teach the chatbot how to understand dates and times. For example, if the command is deadline return book /by 2/12/2019 1800, the chatbot should understand 2/12/2019 1800 as 2nd of December 2019, 6pm, instead of treating it as just a String.

  • Minimal: Store deadline/event dates as a java.time.LocalDate (or java.time.LocalDateTime) in your task objects. Accept dates in a format such as yyyy-mm-dd (e.g., 2019-10-15) and print in a different format such as MMM dd yyyy (e.g., Oct 15 2019).
  • Stretch goal: Use dates and times in more meaningful ways, e.g., add a command to print deadlines/events occurring on a specific date.
AI Guidance » General: Look out for random deviations

AI can do an action (such as running the tests) the first few times without any issue but run into a problem the next time. At times, wanting to be helpful, it will then try to 'fall back' on alternatives. If you are not monitoring the AI carefully, you might not even realize that AI did something different, and that what it did is not what you wanted.

If you see AI going off-track, stop and issue firmer instructions.



4 Add Increments: A-MoreOOP, A-Packages, A-Gradle, A-JUnit, A-Jar

  • While not required, you are encouraged to use a separate branch for each increment.
    Reason: that way, the master branch will stay in an 'unbroken' state, while work in progress remains in a separate branch until it is ready to be merged.
  • As before, commit, tag, and push after each increment.
Duke A-MoreOOP: Use More OOP

A-MoreOOP

     ↳ Make the code more OOP

Gradually (i.e., in small steps) extract closely related code as classes.

  • Minimal: Extract the following classes:
    • Ui: deals with interactions with the user
    • Storage: deals with loading tasks from the file and saving tasks in the file
    • Parser: deals with making sense of the user command
    • TaskList: contains the task list, e.g., it has operations to add/delete tasks in the list

For example, the code of the main class could look like this:

public class Duke {

    private Storage storage;
    private TaskList tasks;
    private Ui ui;

    public Duke(String filePath) {
        ui = new Ui();
        storage = new Storage(filePath);
        try {
            tasks = new TaskList(storage.load());
        } catch (DukeException e) {
            ui.showLoadingError();
            tasks = new TaskList();
        }
    }

    public void run() {
        //...
    }

    public static void main(String[] args) {
        new Duke("data/tasks.txt").run();
    }
}

Your class names may differ from the ones given above. The design can differ too, as long as you can justify it as good OOP (there is no one correct solution for most design problems, after all).

AI Guidance » A-MoreOOP

This is an increment that lends itself well to an incremental approach. We can ask AI to proceed incrementally, rather than us deciding on the increments. Here is an example:

The target: [=====

Gradually (i.e., in small steps) extract closely related code as classes.

  • Minimal: Extract the following classes:
    • Ui: deals with interactions with the user
    • Storage: deals with loading tasks from the file and saving tasks in the file
    • Parser: deals with making sense of the user command
    • TaskList: contains the task list, e.g., it has operations to add/delete tasks in the list

For example, the code of the main class could look like this:

public class Duke {

    private Storage storage;
    private TaskList tasks;
    private Ui ui;

    public Duke(String filePath) {
        ui = new Ui();
        storage = new Storage(filePath);
        try {
            tasks = new TaskList(storage.load());
        } catch (DukeException e) {
            ui.showLoadingError();
            tasks = new TaskList();
        }
    }

    public void run() {
        //...
    }

    public static void main(String[] args) {
        new Duke("data/tasks.txt").run();
    }
}

Your class names may differ from the ones given above. The design can differ too, as long as you can justify it as good OOP.

=====]

Let's do this iteratively. In each iteration, do the following steps:

  1. Decide the next natural stand-alone increment that moves the code closer to the target.
  2. Implement that increment.
  3. Test it to ensure there are no regressions, using the /test-ui skill.
  4. Commit the changes with a detailed commit message. You have my permission to commit in this repo.
  5. Generate a visual diff using the /present-changes-visually skill. Also explain the rationale for the change and its pros and cons.
  6. Briefly outline the next increment to be done in the next iteration. If there are no more increments worth doing, say so and stop.

Go ahead and do the first iteration.

After the first iteration is done, examine the change and the rationale. If you don't agree with the change at all, ask Codex to discard the commit. If you want the change to be done in a slightly different way (e.g., you prefer a different class name), ask Codex to update the code accordingly and amend the commit as well.

When there are no more changes to be done, you can check if the stretch goal has been met. Then, you can prompt AI to go for it.

Now, let's go for the stretch goal given below. Proceed in the same iterative fashion as before.

[=====

  • Stretch goal: Consider extracting more classes, e.g., *Command classes (i.e., AddCommand, DeleteCommand, ExitCommand etc.) that inherit from an abstract Command class, so that you can write the main logic of the app as follows:
    public void run() {
        ui.showWelcome();
        boolean isExit = false;
        while (!isExit) {
            try {
                String fullCommand = ui.readCommand();
                ui.showLine(); // show the divider line ("_______")
                Command c = Parser.parse(fullCommand);
                c.execute(tasks, ui, storage);
                isExit = c.isExit();
            } catch (DukeException e) {
                ui.showError(e.getMessage());
            } finally {
                ui.showLine();
            }
        }
    }
    

=====]



Why more OOP?


Duke A-Packages: Organize into Packages

A-Packages

     ↳ Divide classes into packages

Organize the classes into suitable Java packages.

Keep src/main/java as the folder, because some tools we'll use later look for the Java source code there by default.

For example, suppose you have the following structure now, and you wish to move Duke.java into a package duke.ui.

[project root] e.g., C:\courses\project\ └── src\ └── main\ └── java\ [source root] └── Duke.java (not in a package)

The correct way to do so is:

[project root] e.g., C:\courses\project\ └── src\ └── main\ └── java\ [source root] └── duke\ └── ui\ └── Duke.java (in package duke.ui)

Do not convert src, main, java into packages. For example, the following is incorrect:

[project root] [source root] └── src\ └── main\ └── java\ └── Duke.java (in package src.main.java)
  • Minimal: put all classes in one package, e.g., duke
  • Stretch goal: divide into multiple packages as the number of classes increases, e.g., duke.task, duke.command
AI Guidance » A-Packages

You can work with the AI to decide how to organize the classes into packages. Here is an example:

The requirement: Organize the classes into suitable Java packages. The src/main/java should be kept as the source root folder.

Suggest a suitable package structure for the classes in the project. Do not implement it yet.

After some back-and-forth with the AI to settle on a package structure, you can ask it to go ahead and implement it.



  • Do the following increments in the given order: after doing A-Gradle, you should use the Gradle option (rather than the IntelliJ option) when doing A-JUnit and A-Jar, because those increments are easier to do using Gradle.

How do I fetch the add-gradle-support branch from my fork?


If your fork doesn't have the add-gradle-support branch ...


Duke A-Gradle: Use Gradle

A-Gradle

     ↳ Automate project builds using Gradle

Use Gradle to automate some of the build tasks of the project, as follows:

  • Gradle support is provided as a separate branch named add-gradle-support in the Duke repo. Merge that branch into your master branch.
%%{init: { 'theme': 'default', 'gitGraph': {'mainBranchName': 'master'}} }%%
gitGraph
commit id: "m1"
branch add-gradle-support
checkout add-gradle-support
commit id: "b1"
commit id: "b2"
checkout master
commit id: "m2"
commit id: "m3"
merge add-gradle-support id: "Merge branch ..."

Requirements for this increment:

  • Minimal: Set up Gradle so that you can build and run your chatbot using Gradle. After doing this, you can move to the next increment.
  • Recommended (to be done at a later time): Be able to run JUnit tests using Gradle (this can only be done after you've reached the A-JUnit increment).
  • Stretch goal (to be done at a later time): Use Gradle to automate more things in your project, as you progress through the project.
AI Guidance » A-Gradle

If you are new to merging branches, you can get AI's help to guide you along. Example:

Gradle support is provided as a separate branch named add-gradle-support. I have fetched that branch and merged it into my master branch. Check if I did it correctly.


Gradle support is supposed to be provided as a separate branch named add-gradle-support.

  1. Check if that branch is in my fork.
  2. I have not fetched or pulled that branch to my local repo. I need to merge that branch into my master branch. What are the steps? Explain each step.

Gradle support is provided as a separate branch named add-gradle-support in my fork. I need to merge that branch into my master branch. Go ahead and do it. Also explain the steps you took.


After merging the branch and setting it up in the IDE:

How do I check if Gradle is working?

When I run my app in IDE, I face this error (see the screenshots I've attached). This happened after I added Gradle support to the project. How do I fix it?



Duke A-JUnit: Add JUnit Tests

A-JUnit

     ↳ Add JUnit tests

Add JUnit tests to verify the behavior of the code.

  • Minimal: Use JUnit to test at least two non-trivial methods from two different classes (if you have multiple classes),
    and ensure they are tested reasonably well (i.e., the test code should try to catch most potential bugs in the target methods).
  • Stretch goal: Use JUnit to test all non-trivial public methods of all classes.

Refer to the JUnit tutorial @SE-EDU/guides to learn how to use JUnit (in the context of this project).

AI Guidance » A-JUnit

If you haven't written JUnit tests before, you may want to write the first 1-2 JUnit tests by hand.

Here's a series of prompts for ramping up the JUnit tests in the codebase.

Add a JUnit test to test one of the methods in the codebase. Choose a method that lends itself well to testing through JUnit. Include all reasonable test cases. Follow Gradle and JUnit conventions as to the file path and naming. Example:

  • Class being tested seedu.duke.Todo: src\main\java\seedu\duke\Todo.java
  • Test class seedu.duke.TodoTest: src\test\java\seedu\duke\TodoTest.java

If names of the test methods are long, you may resort to the following naming convention: featureUnderTest_testScenario_expectedBehavior()

e.g. sortList_emptyList_exceptionThrown() getMember_memberNotFound_nullReturned()

Add tests for all candidate methods that deserve tests in that class.

Test coverage target: Focus JUnit tests on the top ~50% highest-value methods (prioritizing complex, core, or critical business logic).

Go ahead and add more tests based on the above target.

Update the relevant AI documentation to reflect the test coverage target of 50%. Mention that JUnit tests need to be updated after each code change to comply with that target.



Duke A-Jar: Create a JAR File

A-Jar

     ↳ Package the app as a JAR file

Package the app as an executable JAR file so that it can be distributed easily.

You can assume the user will run the JAR file in the following way only:

  1. Copy the JAR file into an empty folder.
  2. Open a command window in that folder.
  3. Run the command java -jar "{filename}.jar" e.g., java -jar "Duke.jar" (i.e., run the command in the same folder as the JAR file).

The double quotes around the filename in the java -jar "{filename}.jar" command are not normally needed, but they are if the filename contains special characters such as spaces or [.

FAQ: Can we double-click the JAR file to run it?
A: Yes, that usually works too, but being able to do so is not a requirement here. Instead, the java -jar command is the recommended way to run the JAR file.

Refer to the tutorial Working with JAR files @SE-EDU/guides to learn how to create JAR files (in the context of this project).

If your project is being revision-controlled using Git/GitHub:

  • Do not commit the JAR file created. Reason: We don't normally commit generated binary files into the repository.

  • Instead, make the JAR file available through a GitHub release:

    1. Go to your fork on GitHub and create a new release.
    2. In the page where you supply the details of the release,
      1. give an appropriate version number, e.g., v0.1
      2. attach the JAR file where it says Attach binaries by dropping them ....
AI Guidance » A-Jar

How do I update the build.gradle to create a fat JAR file for this project using the shadowJar plugin? Also explain how to create, locate, and run that JAR file.


Update the relevant files so that I can create a fat JAR file for this project using the shadowJar plugin. Explain the steps you took. Also explain how to create, locate, and run that JAR file.




Note that if the A-Jar increment does not require any code changes, you may tag the commit at which this was achieved as A-Jar (even if that commit has another tag already). Otherwise, tag the latest commit as usual. In both cases, push the tag to the fork.

5 Add Increments as parallel branches: A-JavaDoc, A-CodingStandard, Level-9

  • Implement these three increments as three branches first (branch names: branch-A-JavaDoc, branch-A-CodingStandard, branch-Level-9), and then merge them one by one. Hopefully, you will encounter some merge conflicts so that you get to practice de-conflicting branches.
%%{init: { 'theme': 'default', 'gitGraph': {'mainBranchName': 'master'}} }%%
gitGraph
commit id: "m1"
commit id: "m2"
branch branch-A-JavaDoc
checkout branch-A-JavaDoc
commit id: "b1c1"
commit id: "b1c2"
checkout master
branch branch-A-CodingStandard
checkout branch-A-CodingStandard
commit id: "b2c1"
checkout master
branch branch-Level-9
checkout branch-Level-9
commit id: "b3c1"
commit id: "b3c2"
checkout master
merge branch-A-JavaDoc tag: "A-JavaDoc"
merge branch-A-CodingStandard tag: "A-CodingStandard"
merge branch-Level-9 tag: "Level-9"
commit id: "m6"
  • The Java and Git standards to follow (for A-CodingStandard) are given on this page.
  • After finishing, assuming you encountered merge conflicts (and some were rather painful to resolve), think of how such conflicts could have been reduced (e.g., by changing the order of merging, or minimizing parallel branches when certain types of changes are being made to the code).

If your code already has Javadoc comments for all public methods, you can simply add the A-JavaDoc tag to the current commit -- no need to create a separate branch for it as stated above.

Duke A-JavaDoc: JavaDoc

A-JavaDoc

     ↳ Add JavaDoc comments

Add JavaDoc comments to the code.

  • Minimal: Add header comments to at least half of the non-private classes and methods.
  • Stretch goal: Add header comments to all non-private classes and methods, and non-trivial private methods.

Duke A-CodingStandard: Follow the Coding Standard

A-CodingStandard

     ↳ Tweak the code to comply with a coding standard

Tweak the code to comply with a given coding standard. From this point onward, ensure any new code you add complies with it too.

SE-EDU guides on configuring the code style in IDEs: IntelliJ IDEA | VS Code

AI Guidance » A-CodingStandard

As you need to follow the Java coding standard for future code, you can turn it into a reusable skill.

Create a project-specific skill named seedu-java-coding-standard based on rules given in https://se-education.org/guides/conventions/java/intermediate.html

Update your agent files to mandate following this for all code in this project.

Update the current code to follow it, where necessary.

Show me the changes using the /present-changes-visually skill.

Same goes for the Git commit message standard:

Create a project-specific skill named seedu-git-standard based on rules given in https://se-education.org/guides/conventions/git.html

Update your agent files to mandate following this for all future commits.

Next, you can do the following to check if the Git standard is followed correctly.

Propose a commit message for the uncommitted Java code changes.

If everything seems OK, you can proceed with the commits. If not, ask AI to update the skill where you see it is not following the standard.

Create one commit for the Java code changes, and one commit for each standalone change to agent files.



FAQ What to do if a certain aspect is not covered by the Java/Git standard?


Duke Level-9: Find

Level 9. Find

Give users a way to find a task by searching for a keyword in the task description.

Example:

find book
    ____________________________________________________________
     Here are the matching tasks in your list:
     1.[T][X] read book
     2.[D][X] return book (by: June 6th)
    ____________________________________________________________
AI Guidance » Level 9. Find

As this increment is fairly straightforward, breaking it into smaller steps might not have much learning value. You can try to 'one shot' it: give the AI all the information up front and ask it to complete the increment entirely, including committing, tagging, and pushing. If the AI misses parts of the expected work, you can learn from that and try to be more comprehensive next time.



6 [Optional] Get a head start on JavaFX

If you can spare some time, start learning JavaFX by following the JavaFX tutorial @SE-EDU/guides. Doing so will make your life easier in the following week.

JavaFX Tutorial Submission: [required for CP3108A/B, optional for the rest]:

  1. Fork the starter repo, as instructed in the JavaFX Tutorial Part 1.
  2. As you go through the tutorial, commit regularly. After finishing a tutorial part, add matching tags Tutorial-Part1, Tutorial-Part2, ... Tutorial-Part5 (all five required for CP3108A/B).
  3. Push the commits and tags to your fork.
  4. Also complete, commit, tag, and push at least half of the tweaks shown in Part 5 of the tutorial.
AI Guidance » JavaFX Tutorial

You can use AI to go through the JavaFX tutorial the same way you've been doing so far in the project.

For example, after setting up the starter project given in the JavaFX tutorial in a local folder, you can start a Codex project in that folder. Next, you can use a prompt like this:

See the JavaFX tutorial given at https://se-education.org/guides/tutorials/javaFxPart1.html

Implement the HelloWorld application as described in that page.

How much you use AI depends on how deeply you want to internalize JavaFX concepts. If you don't expect to need JavaFX in the future, you can go for maximum use of AI.

If you wish to use the present-changes-visually skill in this project as well, you can issue a prompt like this:

Promote the skill present-changes-visually I created in the project [give the project folder here] into a system-wide skill so that I can use it from other projects.

You might have to restart Codex to make the promoted skill available for use.



iP: Week 2iP: Week 4