diff --git a/data/plannedbook.json b/data/plannedbook.json index 1da4f122cb7..9a472d1d071 100644 --- a/data/plannedbook.json +++ b/data/plannedbook.json @@ -1,17 +1,42 @@ { "plannedRecipes" : [ { "recipes" : [ { - "name" : "Asian BBQ Chicken", + "name" : "testest", "time" : "15", + "isFavourite" : true, + "grains" : [ "50.0g, grain", "30.0g, rice" ], + "vegetables" : [ ], + "proteins" : [ ], + "fruits" : [ "50.0g, fruit" ], + "others" : [ ], + "steps" : [ "step 1", "edited step 2" ], + "goals" : [ ] + }, { + "name" : "Quick Carrot Daikon Stir-fry", + "time" : "25", "isFavourite" : false, - "grains" : [ ], - "vegetables" : [ "1.0tbsp, garlic & ginger (finely chopped)" ], - "proteins" : [ "100.0g, chicken breast" ], + "grains" : [ "30.0g, grain" ], + "vegetables" : [ "30.0g, carrot (julienned)", "500.0g, Daikon radish (peeled and julienned)", "30.0g, garlic clove (smashed and chopped)", "20.0g, ginger (julienned sliced)", "30.0g, scallions" ], + "proteins" : [ ], "fruits" : [ ], - "others" : [ "2.0tbsp, char siew sauce", "1.0tsp, honey", "1.0tsp, rice vinegar", "1.0tsp, Salt & pepper", "0.5tsp, sesame oil", "50.0g, sugarspiceeverythingnice" ], - "steps" : [ "In a bowl, mix all the ingredients to create marinade. Mix the chicken with the marinade.", "Pan sear the chicken from both sides.", "edited step 3", "hi" ], - "goals" : [ "Bulk like the Hulk" ] + "others" : [ "50.0g, other", "50.0g, others", "1.5tbsp, oyster sauce", "0.5tsp, salt", "0.5tsp, sesame oil", "1.0tbsp, shaoxing wine", "3.0tbsp, vegetable oil", "0.25cup, water", "0.25tsp, white pepper" ], + "steps" : [ "Heat oil in wok over medium heat. Add ginger and garlic and cook for 20 seconds.", "Add carrot and stir-fry for 30 seconds - oil should turn orange colour. Add in daikon, turn up heat to high and stir-fry for another 30 seconds.", "Add shaoxing wine, oyster sauce, white pepper, sesame oil, salt, water, and white parts of the scallions. Stir to combine, cover, turn down heat to medium. Simmer for 5-8 minutes until tender.", "Uncover, add in rest of the scallions and mix everything well. When dailon is translucent and tender, the dish is done." ], + "goals" : [ "Herbivore" ] } ], - "date" : "2020-05-02" + "date" : "2020-04-03" + }, { + "recipes" : [ { + "name" : "testest", + "time" : "15", + "isFavourite" : true, + "grains" : [ "50.0g, grain", "30.0g, rice" ], + "vegetables" : [ ], + "proteins" : [ ], + "fruits" : [ "50.0g, fruit" ], + "others" : [ ], + "steps" : [ "step 1", "edited step 2" ], + "goals" : [ ] + } ], + "date" : "2020-04-04" } ] } diff --git a/data/recipebook.json b/data/recipebook.json index 4f3a7f0307a..49968e2ea6f 100644 --- a/data/recipebook.json +++ b/data/recipebook.json @@ -1,35 +1,13 @@ { "recipes" : [ { - "name" : "Asian BBQ Chicken", - "time" : "15", - "isFavourite" : false, - "grains" : [ ], - "vegetables" : [ "1.0tbsp, garlic & ginger (finely chopped)" ], - "proteins" : [ "100.0g, chicken breast" ], - "fruits" : [ ], - "others" : [ "2.0tbsp, char siew sauce", "1.0tsp, honey", "1.0tsp, rice vinegar", "1.0tsp, Salt & pepper", "0.5tsp, sesame oil", "50.0g, sugarspiceeverythingnice" ], - "steps" : [ "In a bowl, mix all the ingredients to create marinade. Mix the chicken with the marinade.", "Pan sear the chicken from both sides.", "edited step 3", "hi" ], - "goals" : [ "Bulk like the Hulk" ] - }, { - "name" : "Sweet and Sour Chicken", - "time" : "20", - "isFavourite" : false, - "grains" : [ ], - "vegetables" : [ "1.0cup, bell peppers", "1.0cup, carrots (diced)", "1.0cup, chinese cabbage (diced)", "10.0g, garlic cloves", "10.0g, ginger", "1.0cup, onions (diced)" ], - "proteins" : [ "300.0g, chicken", "50.0g, egg" ], - "fruits" : [ "1.0cup, pineapple (diced)", "1.0tbsp, tomato juice" ], - "others" : [ "10.0g, Coriander (optional)", "1.5tsp, corn flour", "1.0tsp, light soya sauce", "1.0tbsp, vegetable oil" ], - "steps" : [ "Pound garlic and ginger into a paste. Marinade chicken in the paste, along with corn flour, soy sauce, and salt.", "In a wok, take some oil, saute the carrots, cabbage, onions, and bell peppers. Add tomato juice and bring to a slight boil.", "Fry the chicken and add to the wok along with some stock and set to simmer.", "Garnish with coriander and serve with rice." ], - "goals" : [ "Bulk like the Hulk" ] - }, { "name" : "Quick Carrot Daikon Stir-fry", "time" : "25", "isFavourite" : false, - "grains" : [ ], - "vegetables" : [ "60.0g, carrot (julienned)", "500.0g, Daikon radish (peeled and julienned)", "30.0g, garlic clove (smashed and chopped)", "20.0g, ginger (julienned sliced)", "30.0g, scallions" ], + "grains" : [ "30.0g, grain" ], + "vegetables" : [ "30.0g, carrot (julienned)", "500.0g, Daikon radish (peeled and julienned)", "30.0g, garlic clove (smashed and chopped)", "20.0g, ginger (julienned sliced)", "30.0g, scallions" ], "proteins" : [ ], "fruits" : [ ], - "others" : [ "1.5tbsp, oyster sauce", "0.5tsp, salt", "0.5tsp, sesame oil", "1.0tbsp, shaoxing wine", "3.0tbsp, vegetable oil", "0.25cup, water", "0.25tsp, white pepper" ], + "others" : [ "50.0g, other", "50.0g, others", "1.5tbsp, oyster sauce", "0.5tsp, salt", "0.5tsp, sesame oil", "1.0tbsp, shaoxing wine", "3.0tbsp, vegetable oil", "0.25cup, water", "0.25tsp, white pepper" ], "steps" : [ "Heat oil in wok over medium heat. Add ginger and garlic and cook for 20 seconds.", "Add carrot and stir-fry for 30 seconds - oil should turn orange colour. Add in daikon, turn up heat to high and stir-fry for another 30 seconds.", "Add shaoxing wine, oyster sauce, white pepper, sesame oil, salt, water, and white parts of the scallions. Stir to combine, cover, turn down heat to medium. Simmer for 5-8 minutes until tender.", "Uncover, add in rest of the scallions and mix everything well. When dailon is translucent and tender, the dish is done." ], "goals" : [ "Herbivore" ] }, { @@ -142,5 +120,16 @@ "others" : [ ], "steps" : [ "Boil chicken", "While chicken is cooking, add sesame oil and crushed ginger into rice and cook it", "When chicken is done, dip it into iced water", "Serve while rice is hot" ], "goals" : [ "Bulk like the Hulk" ] + }, { + "name" : "testest", + "time" : "15", + "isFavourite" : true, + "grains" : [ "50.0g, grain", "30.0g, rice" ], + "vegetables" : [ ], + "proteins" : [ ], + "fruits" : [ "50.0g, fruit" ], + "others" : [ ], + "steps" : [ "step 1", "edited step 2" ], + "goals" : [ ] } ] } diff --git a/src/main/java/seedu/recipe/MainApp.java b/src/main/java/seedu/recipe/MainApp.java index 68b860e7492..365cefd04cc 100644 --- a/src/main/java/seedu/recipe/MainApp.java +++ b/src/main/java/seedu/recipe/MainApp.java @@ -18,13 +18,13 @@ import seedu.recipe.model.Model; import seedu.recipe.model.ModelManager; import seedu.recipe.model.ReadOnlyCookedRecordBook; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.ReadOnlyUserPrefs; import seedu.recipe.model.RecipeBook; import seedu.recipe.model.UserPrefs; import seedu.recipe.model.cooked.CookedRecordBook; import seedu.recipe.model.plan.PlannedBook; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; import seedu.recipe.model.util.SampleDataUtil; import seedu.recipe.storage.CookedRecordBookStorage; import seedu.recipe.storage.JsonCookedRecordBookStorage; diff --git a/src/main/java/seedu/recipe/logic/Logic.java b/src/main/java/seedu/recipe/logic/Logic.java index 5838f3e3fdc..2a811136f72 100644 --- a/src/main/java/seedu/recipe/logic/Logic.java +++ b/src/main/java/seedu/recipe/logic/Logic.java @@ -9,7 +9,7 @@ import seedu.recipe.logic.parser.exceptions.ParseException; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.cooked.Record; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; /** @@ -59,6 +59,6 @@ public interface Logic { /** * Returns an unmodifiable view of the scheduled recipes. */ - ObservableList getFilteredPlannedList(); + ObservableList getFilteredPlannedList(); } diff --git a/src/main/java/seedu/recipe/logic/LogicManager.java b/src/main/java/seedu/recipe/logic/LogicManager.java index e6ac284a7c6..d2feb1d6cf2 100644 --- a/src/main/java/seedu/recipe/logic/LogicManager.java +++ b/src/main/java/seedu/recipe/logic/LogicManager.java @@ -15,7 +15,7 @@ import seedu.recipe.model.Model; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.cooked.Record; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; import seedu.recipe.storage.Storage; @@ -86,7 +86,7 @@ public ObservableList getFilteredRecordList() { } @Override - public ObservableList getFilteredPlannedList() { + public ObservableList getFilteredPlannedList() { return model.getFilteredPlannedList(); } diff --git a/src/main/java/seedu/recipe/logic/commands/AddIngredientCommand.java b/src/main/java/seedu/recipe/logic/commands/AddIngredientCommand.java index 00e9dfe6e5a..75cad61f698 100644 --- a/src/main/java/seedu/recipe/logic/commands/AddIngredientCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/AddIngredientCommand.java @@ -82,7 +82,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/AddStepCommand.java b/src/main/java/seedu/recipe/logic/commands/AddStepCommand.java index 9268ef51276..16a20c4b0a6 100644 --- a/src/main/java/seedu/recipe/logic/commands/AddStepCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/AddStepCommand.java @@ -65,7 +65,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/CookedCommand.java b/src/main/java/seedu/recipe/logic/commands/CookedCommand.java index f5e469dc392..aa832241ee6 100644 --- a/src/main/java/seedu/recipe/logic/commands/CookedCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/CookedCommand.java @@ -29,9 +29,11 @@ public class CookedCommand extends Command { private final Index[] targetIndex; private final Tab goalsTab = Tab.GOALS; + private final CommandType commandType; public CookedCommand(Index[] targetIndex) { this.targetIndex = targetIndex; + this.commandType = CommandType.GOALS; } @Override @@ -59,7 +61,7 @@ public CommandResult execute(Model model) throws CommandException { } } sb.append("!"); - model.commitRecipeBook(); + model.commitBook(commandType); return new CommandResult(sb.toString(), false, goalsTab, false); } diff --git a/src/main/java/seedu/recipe/logic/commands/DeleteCommand.java b/src/main/java/seedu/recipe/logic/commands/DeleteCommand.java index e0fb655a649..19874a9da33 100644 --- a/src/main/java/seedu/recipe/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/DeleteCommand.java @@ -45,7 +45,6 @@ public CommandResult execute(Model model) throws CommandException { Index indexAfterEachDeletion = Index.fromZeroBased(targetIndex[i].getZeroBased() - i); Recipe recipeToDelete = lastShownList.get(indexAfterEachDeletion.getZeroBased()); model.deleteRecipe(recipeToDelete); - model.deleteAllPlansFor(recipeToDelete); if (i == targetIndex.length - 1 && targetIndex.length != 1) { sb.append(" and "); } diff --git a/src/main/java/seedu/recipe/logic/commands/DeleteIngredientCommand.java b/src/main/java/seedu/recipe/logic/commands/DeleteIngredientCommand.java index ddbe02b1d4b..1b40d33c63c 100644 --- a/src/main/java/seedu/recipe/logic/commands/DeleteIngredientCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/DeleteIngredientCommand.java @@ -94,7 +94,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/DeleteStepCommand.java b/src/main/java/seedu/recipe/logic/commands/DeleteStepCommand.java index 20e78d6c21e..6a497b32fdb 100644 --- a/src/main/java/seedu/recipe/logic/commands/DeleteStepCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/DeleteStepCommand.java @@ -70,7 +70,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/EditCommand.java b/src/main/java/seedu/recipe/logic/commands/EditCommand.java index 3da36084b78..322c3ddf337 100644 --- a/src/main/java/seedu/recipe/logic/commands/EditCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/EditCommand.java @@ -108,7 +108,6 @@ public CommandResult execute(Model model) throws CommandException { model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); return new CommandResult(String.format(MESSAGE_EDIT_RECIPE_SUCCESS, editedRecipe)); diff --git a/src/main/java/seedu/recipe/logic/commands/EditIngredientCommand.java b/src/main/java/seedu/recipe/logic/commands/EditIngredientCommand.java index 2c333d4de4c..cafcf18ef19 100644 --- a/src/main/java/seedu/recipe/logic/commands/EditIngredientCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/EditIngredientCommand.java @@ -86,7 +86,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/EditStepCommand.java b/src/main/java/seedu/recipe/logic/commands/EditStepCommand.java index 6350b7e9700..106aa7aa7ea 100644 --- a/src/main/java/seedu/recipe/logic/commands/EditStepCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/EditStepCommand.java @@ -73,7 +73,6 @@ public CommandResult execute(Model model) throws CommandException { Recipe editedRecipe = createEditedRecipe(recipeToEdit, editRecipeDescriptor); model.setRecipe(recipeToEdit, editedRecipe); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); - model.setRecipeInPlans(recipeToEdit, editedRecipe); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/FavouriteCommand.java b/src/main/java/seedu/recipe/logic/commands/FavouriteCommand.java index f7c1ac9b4cc..00699cb3aea 100644 --- a/src/main/java/seedu/recipe/logic/commands/FavouriteCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/FavouriteCommand.java @@ -55,6 +55,7 @@ public CommandResult execute(Model model) throws CommandException { } sb.append(" to favourites!"); model.updateFilteredRecipeList(PREDICATE_SHOW_ALL_RECIPES); + model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); return new CommandResult(sb.toString()); diff --git a/src/main/java/seedu/recipe/logic/commands/plan/DeletePlanCommand.java b/src/main/java/seedu/recipe/logic/commands/plan/DeletePlanCommand.java index c461543b65b..88f7c94e75f 100644 --- a/src/main/java/seedu/recipe/logic/commands/plan/DeletePlanCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/plan/DeletePlanCommand.java @@ -14,7 +14,7 @@ import seedu.recipe.logic.commands.exceptions.CommandException; import seedu.recipe.model.Date; import seedu.recipe.model.Model; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.plan.PlannedRecipeOnDatePredicate; import seedu.recipe.model.recipe.Recipe; import seedu.recipe.ui.tab.Tab; @@ -56,20 +56,20 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); model.updateFilteredPlannedList(new PlannedRecipeOnDatePredicate(atDate)); - List plannedListAtDate = model.getFilteredPlannedList(); + List plannedListAtDate = model.getFilteredPlannedList(); if (plannedListAtDate.size() != 1) { throw new CommandException(Messages.MESSAGE_INVALID_PLANNED_DATE); } - PlannedRecipe plannedRecipeToEdit = plannedListAtDate.get(0); - List recipesAtPlannedDate = plannedRecipeToEdit.getRecipes(); + PlannedDate plannedDateToEdit = plannedListAtDate.get(0); + List recipesAtPlannedDate = plannedDateToEdit.getRecipes(); if (plannedIndex.getOneBased() > recipesAtPlannedDate.size()) { throw new CommandException(Messages.MESSAGE_INVALID_PLANNED_RECIPE_DISPLAYED_INDEX); } Recipe recipeToDelete = recipesAtPlannedDate.get(plannedIndex.getZeroBased()); - model.deleteRecipeFromOnePlan(recipeToDelete, plannedRecipeToEdit); + model.deleteOnePlan(recipeToDelete, plannedDateToEdit); model.updateFilteredPlannedList(PREDICATE_SHOW_ALL_PLANNED_RECIPES); model.commitBook(commandType); diff --git a/src/main/java/seedu/recipe/logic/commands/plan/PlanCommand.java b/src/main/java/seedu/recipe/logic/commands/plan/PlanCommand.java index f8a580d6c5b..0cecbd0fa04 100644 --- a/src/main/java/seedu/recipe/logic/commands/plan/PlanCommand.java +++ b/src/main/java/seedu/recipe/logic/commands/plan/PlanCommand.java @@ -16,7 +16,7 @@ import seedu.recipe.model.Date; import seedu.recipe.model.Model; import seedu.recipe.model.plan.DuplicatePlannedRecipeException; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; import seedu.recipe.ui.tab.Tab; @@ -69,10 +69,10 @@ public CommandResult execute(Model model) throws CommandException { List recipesToPlan = new ArrayList<>(); recipesToPlan.add(recipeToPlan); - PlannedRecipe plannedRecipe = new PlannedRecipe(recipesToPlan, atDate); + PlannedDate plannedDate = new PlannedDate(recipesToPlan, atDate); try { - model.addPlanForOneRecipe(recipeToPlan, plannedRecipe); + model.addOnePlan(recipeToPlan, plannedDate); } catch (DuplicatePlannedRecipeException dp) { throw new CommandException(String.format(MESSAGE_DUPLICATE_PLANNED_RECIPE, atDate.toString(), index.getOneBased())); diff --git a/src/main/java/seedu/recipe/logic/parser/plan/PlanCommandParser.java b/src/main/java/seedu/recipe/logic/parser/plan/PlanCommandParser.java index 54cd1c44e40..810773e30ba 100644 --- a/src/main/java/seedu/recipe/logic/parser/plan/PlanCommandParser.java +++ b/src/main/java/seedu/recipe/logic/parser/plan/PlanCommandParser.java @@ -41,7 +41,7 @@ public PlanCommand parse(String args) throws ParseException { } Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); - if (!date.isFutureDate()) { + if (!date.isDateInFuture()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PlanCommand.MESSAGE_INVALID_DATE)); } diff --git a/src/main/java/seedu/recipe/model/Date.java b/src/main/java/seedu/recipe/model/Date.java index 94090ede449..52c5a52ec1f 100644 --- a/src/main/java/seedu/recipe/model/Date.java +++ b/src/main/java/seedu/recipe/model/Date.java @@ -52,7 +52,7 @@ public Date() { * Returns true if a given string is a valid date */ public static boolean isValidDate(String test) { - if (!test.matches(VALIDATION_REGEX)) { + if (test.isBlank() || !test.matches(VALIDATION_REGEX)) { return false; } try { @@ -75,7 +75,7 @@ public static Date today() { /** * Returns true if the date is older than today's date, according to the input timezone. */ - public boolean isFutureDate() { + public boolean isDateInFuture() { Date yesterday = new Date(LocalDate.now(ZONE_ID).minusDays(1)); return isAfter(yesterday); } @@ -85,7 +85,10 @@ public boolean isFutureDate() { * The start and end dates are non-inclusive. */ public boolean isWithinRange(Date start, Date end) { - return date.isAfter(start.date) && date.isBefore(end.date); + if (!start.isAfter(end)) { + return date.isAfter(start.date) && date.isBefore(end.date); + } + return false; } /** diff --git a/src/main/java/seedu/recipe/model/Model.java b/src/main/java/seedu/recipe/model/Model.java index 30807339d51..f9b60232983 100644 --- a/src/main/java/seedu/recipe/model/Model.java +++ b/src/main/java/seedu/recipe/model/Model.java @@ -8,8 +8,7 @@ import seedu.recipe.commons.core.GuiSettings; import seedu.recipe.logic.commands.CommandType; import seedu.recipe.model.cooked.Record; -import seedu.recipe.model.plan.PlannedRecipe; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; /** @@ -20,7 +19,7 @@ public interface Model { Predicate PREDICATE_SHOW_ALL_RECIPES = unused -> true; /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PLANNED_RECIPES = unused -> true; + Predicate PREDICATE_SHOW_ALL_PLANNED_RECIPES = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -141,24 +140,24 @@ public interface Model { void setPlannedBook(ReadOnlyPlannedBook plannedBook); /** - * Adds the {@code plannedRecipe} that plans for {@code recipe}. + * Adds the {@code plannedDate} that plans for {@code recipe}. */ - void addPlanForOneRecipe(Recipe recipe, PlannedRecipe plannedRecipe); + void addOnePlan(Recipe recipe, PlannedDate plannedDate); /** - * Adds the {@code plannedRecipe} that plans for all {@code recipes}. + * Adds the {@code plannedDate} that plans for all {@code recipes}. */ - void addPlanForAllRecipes(List recipes, PlannedRecipe plannedRecipe); + void addAllRecipesToPlan(List recipes, PlannedDate plannedDate); /** * Deletes the {@code recipe} plan. */ - void deleteRecipeFromOnePlan(Recipe recipe, PlannedRecipe plannedRecipe); + void deleteOnePlan(Recipe recipe, PlannedDate plannedDate); /** * Deletes all plans for {@code recipe}. */ - void deleteAllPlansFor(Recipe recipe); + void deleteAllRecipePlans(Recipe recipe); /** * Updates the recipe in the planned recipes from {@code target} to {@code editedRecipe}. @@ -168,12 +167,12 @@ public interface Model { /** * Returns an unmodifiable view of the planned recipes. */ - ObservableList getFilteredPlannedList(); + ObservableList getFilteredPlannedList(); /** * Updates the filtered planned list to be filtered using the {@code predicate}. //todo: throw exception */ - void updateFilteredPlannedList(Predicate predicate); + void updateFilteredPlannedList(Predicate predicate) throws NullPointerException; //updateAndFillPlannedList //todo: implement for weekly view diff --git a/src/main/java/seedu/recipe/model/ModelManager.java b/src/main/java/seedu/recipe/model/ModelManager.java index b66255f102c..940155cd378 100644 --- a/src/main/java/seedu/recipe/model/ModelManager.java +++ b/src/main/java/seedu/recipe/model/ModelManager.java @@ -16,8 +16,7 @@ import seedu.recipe.model.cooked.CookedRecordBook; import seedu.recipe.model.cooked.Record; import seedu.recipe.model.plan.PlannedBook; -import seedu.recipe.model.plan.PlannedRecipe; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; /** @@ -33,7 +32,7 @@ public class ModelManager implements Model { private final MultipleBookStateManager states; private final CookedRecordBook cookedRecordBook; private final FilteredList filteredRecords; - private final FilteredList filteredPlannedRecipes; + private final FilteredList filteredPlannedDates; /** @@ -53,7 +52,7 @@ public ModelManager(ReadOnlyRecipeBook recipeBook, ReadOnlyUserPrefs userPrefs, this.cookedRecordBook = new CookedRecordBook(cookedRecordBook); this.filteredRecords = new FilteredList<>(this.cookedRecordBook.getRecordsList()); this.states = new MultipleBookStateManager(recipeBook, plannedBook, cookedRecordBook); - filteredPlannedRecipes = new FilteredList<>(this.plannedBook.getPlannedList()); + filteredPlannedDates = new FilteredList<>(this.plannedBook.getPlannedList()); } public ModelManager() { @@ -117,16 +116,19 @@ public boolean hasRecipe(Recipe recipe) { @Override public void deleteRecipe(Recipe target) { recipeBook.removeRecipe(target); + plannedBook.deleteAllRecipePlans(target); } @Override public void favouriteRecipe(Recipe target) { recipeBook.favouriteRecipe(target); + plannedBook.favouriteRecipe(target); } @Override public void unfavouriteRecipe(Recipe target) { recipeBook.unfavouriteRecipe(target); + plannedBook.unfavouriteRecipe(target); } @Override @@ -139,6 +141,7 @@ public void addRecipe(Recipe recipe) { public void setRecipe(Recipe target, Recipe editedRecipe) { requireAllNonNull(target, editedRecipe); recipeBook.setRecipe(target, editedRecipe); + plannedBook.setRecipe(target, editedRecipe); } @Override @@ -214,39 +217,39 @@ public void setPlannedBook(ReadOnlyPlannedBook plannedBook) { //=========== Plan Recipe List Accessors ============================================================= @Override - public void addPlanForOneRecipe(Recipe recipe, PlannedRecipe plannedRecipe) { - plannedBook.addPlanForOneRecipe(recipe, plannedRecipe); + public void addOnePlan(Recipe recipe, PlannedDate plannedDate) { + plannedBook.addOnePlan(recipe, plannedDate); } @Override - public void addPlanForAllRecipes(List recipes, PlannedRecipe plannedRecipe) { - plannedBook.addPlanForAllRecipes(recipes, plannedRecipe); + public void addAllRecipesToPlan(List recipes, PlannedDate plannedDate) { + plannedBook.addAllRecipesToPlan(recipes, plannedDate); } @Override - public void deleteRecipeFromOnePlan(Recipe recipe, PlannedRecipe plannedRecipe) { - plannedBook.deleteRecipeFromPlannedRecipe(recipe, plannedRecipe); + public void deleteOnePlan(Recipe recipe, PlannedDate plannedDate) { + plannedBook.deleteOnePlan(recipe, plannedDate); } @Override - public void deleteAllPlansFor(Recipe recipe) { - plannedBook.deleteAllPlansFor(recipe); + public void deleteAllRecipePlans(Recipe recipe) { + plannedBook.deleteAllRecipePlans(recipe); } @Override public void setRecipeInPlans(Recipe target, Recipe editedRecipe) { - plannedBook.setRecipeInPlans(target, editedRecipe); + plannedBook.setRecipe(target, editedRecipe); } @Override - public ObservableList getFilteredPlannedList() { - return filteredPlannedRecipes; + public ObservableList getFilteredPlannedList() { + return filteredPlannedDates; } @Override - public void updateFilteredPlannedList(Predicate predicate) { + public void updateFilteredPlannedList(Predicate predicate) { requireNonNull(predicate); - filteredPlannedRecipes.setPredicate(predicate); + filteredPlannedDates.setPredicate(predicate); } diff --git a/src/main/java/seedu/recipe/model/MultipleBookStateManager.java b/src/main/java/seedu/recipe/model/MultipleBookStateManager.java index 9eeb2fe7854..84b87cfe4d4 100644 --- a/src/main/java/seedu/recipe/model/MultipleBookStateManager.java +++ b/src/main/java/seedu/recipe/model/MultipleBookStateManager.java @@ -4,7 +4,6 @@ import java.util.Stack; import seedu.recipe.logic.commands.CommandType; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; /** * Maintains different versions (states) of RecipeBooks, PlannedBooks, and CookedRecordBooks in a list. diff --git a/src/main/java/seedu/recipe/model/plan/ReadOnlyPlannedBook.java b/src/main/java/seedu/recipe/model/ReadOnlyPlannedBook.java similarity index 55% rename from src/main/java/seedu/recipe/model/plan/ReadOnlyPlannedBook.java rename to src/main/java/seedu/recipe/model/ReadOnlyPlannedBook.java index f479e9ba7e7..c6e842d68cd 100644 --- a/src/main/java/seedu/recipe/model/plan/ReadOnlyPlannedBook.java +++ b/src/main/java/seedu/recipe/model/ReadOnlyPlannedBook.java @@ -1,10 +1,8 @@ -package seedu.recipe.model.plan; - -import java.util.List; -import java.util.Map; +package seedu.recipe.model; import javafx.collections.ObservableList; -import seedu.recipe.model.recipe.Recipe; +import seedu.recipe.model.plan.PlannedDate; +import seedu.recipe.model.plan.PlannedRecipeMap; /** * Unmodifiable view of a schedule book @@ -15,8 +13,8 @@ public interface ReadOnlyPlannedBook { * Returns an unmodifiable view of the planned recipes map. * This map will not contain duplicate recipes on the same day. */ - ObservableList getPlannedList(); + ObservableList getPlannedList(); - Map> getPlannedRecipeMap(); + PlannedRecipeMap getPlannedMap(); } diff --git a/src/main/java/seedu/recipe/model/plan/PlannedBook.java b/src/main/java/seedu/recipe/model/plan/PlannedBook.java index 68ba9845442..def195ac372 100644 --- a/src/main/java/seedu/recipe/model/plan/PlannedBook.java +++ b/src/main/java/seedu/recipe/model/plan/PlannedBook.java @@ -3,26 +3,24 @@ import static java.util.Objects.requireNonNull; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javafx.collections.ObservableList; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.recipe.Recipe; /** * Wraps all data at the planned-recipe-book level * Duplicates are not allowed (by .isSameRecipe comparison) - * //todo, shift the Map to another class */ public class PlannedBook implements ReadOnlyPlannedBook { - private final UniquePlannedList plannedRecipes; - private final Map> plannedRecipeMap; + private final UniquePlannedList plannedDates; + private final PlannedRecipeMap recipeMap; public PlannedBook() { - plannedRecipes = new UniquePlannedList(); - plannedRecipeMap = new HashMap<>(); + plannedDates = new UniquePlannedList(); + recipeMap = new PlannedRecipeMap(); } /** @@ -35,19 +33,18 @@ public PlannedBook(ReadOnlyPlannedBook toBeCopied) { /** - * Replaces the contents of the planned recipe list with {@code plannedRecipes}. - * {@code plannedRecipes} must not contain duplicate recipes on the same day. + * Replaces the contents of the planned recipe list with {@code plannedDates}. + * {@code plannedDates} must not contain duplicate recipes on the same day. */ - public void setPlannedRecipes(ObservableList plannedRecipes) { - this.plannedRecipes.setPlannedRecipes(plannedRecipes); + public void setPlannedDates(ObservableList plannedDates) { + this.plannedDates.setPlannedDates(plannedDates); } /** - * Replaces the contents of the planned recipe to recipe mapping with {@code plannedRecipes}. + * Replaces the contents of the recipe map with {@code plannedRecipeMap}. */ - public void setPlannedRecipeMap(Map> plannedRecipeMap) { - this.plannedRecipeMap.clear(); - this.plannedRecipeMap.putAll(plannedRecipeMap); + public void setRecipeMap(PlannedRecipeMap recipeMap) { + this.recipeMap.setPlannedRecipeMap(recipeMap); } /** @@ -56,150 +53,131 @@ public void setPlannedRecipeMap(Map> plannedRecipeMa public void resetData(ReadOnlyPlannedBook newData) { requireNonNull(newData); - setPlannedRecipes(newData.getPlannedList()); - setPlannedRecipeMap(newData.getPlannedRecipeMap()); + setPlannedDates(newData.getPlannedList()); + setRecipeMap(newData.getPlannedMap()); } - // ===== PlannedRecipe-level methods ===== + // ===== PlannedDate-level methods ===== /** - * Checks whether the planned book contains {@code plannedRecipe}. + * Checks whether the planned book contains {@code plannedDate}. */ - public boolean contains(PlannedRecipe plannedRecipe) { - return plannedRecipes.hasPlannedRecipe(plannedRecipe); + public boolean contains(PlannedDate plannedDate) { + return plannedDates.hasPlannedDate(plannedDate); } /** - * Adds a {@code plannedRecipe} into plannedRecipes. + * Adds {@code plannedDate} into the planned recipes and recipe map. * The planned recipe must not exist in the planned book. - * Adds the mapping from {@code recipe} to {@code plannedRecipe} as well. */ - public void addPlanForOneRecipe(Recipe recipe, PlannedRecipe plannedRecipe) { - plannedRecipes.add(plannedRecipe); - addPlannedMapping(recipe, plannedRecipe); + public void addOnePlan(Recipe recipe, PlannedDate plannedDate) { + plannedDates.add(plannedDate); + recipeMap.addOnePlannedRecipe(recipe, plannedDate); } /** - * Adds a {@code plannedRecipe} into plannedRecipes. + * Updates the recipe map for all {@code recipes}. + * Adds {@code plannedDate} into the planned recipes list. * The planned recipe must not exist in the planned book. - * Adds the mapping from all {@code recipe} to {@code plannedRecipe} as well. */ - public void addPlanForAllRecipes(List recipes, PlannedRecipe plannedRecipe) { - plannedRecipes.add(plannedRecipe); - addAllPlannedMapping(recipes, plannedRecipe); + public void addAllRecipesToPlan(List recipes, PlannedDate plannedDate) { + plannedDates.add(plannedDate); + recipeMap.addAllRecipesToPlan(recipes, plannedDate); } /** - * Deletes {@code recipe} from all planned recipes. - * Deletes all the {@code recipe} key in the mapping as well. + * Deletes {@code recipe} from all planned dates in the planned recipes list and map. */ - public void deleteAllPlansFor(Recipe recipe) { - if (plannedRecipeMap.containsKey(recipe)) { - List plans = new ArrayList<>(); - plans.addAll(plannedRecipeMap.get(recipe)); - for (PlannedRecipe plannedRecipe : plans) { - deleteRecipeFromPlannedRecipe(recipe, plannedRecipe); + public void deleteAllRecipePlans(Recipe recipe) { + List plans = new ArrayList<>(recipeMap.getPlans(recipe)); + recipeMap.deleteAllPlannedRecipes(recipe); + for (PlannedDate plan: plans) { + if (plan.isOneRecipe()) { + plannedDates.remove(plan); // delete planned date if it consisted of that one recipe only + } else { + plannedDates.remove(plan); + PlannedDate newPlannedDate = plan.deleteRecipe(recipe); + plannedDates.add(newPlannedDate); } - plannedRecipeMap.remove(recipe); } } /** - * Deletes one {@code recipe} from the internal list in the {@code plannedRecipe}. - * If the {@code recipe} is the last recipe in the internal list, delete the {@code plannedRecipe} + * Deletes {@code recipe} from the planned recipes internal list in the {@code plannedDate}. + * If the {@code recipe} is the last recipe in the internal list, delete the {@code plannedDate} * from the plannedRecipes list. - * Deletes the mapping from {@code recipe} to {@code plannedRecipe} in the mapping as well. + * Deletes {@code recipe} in the mapping as well. */ - public void deleteRecipeFromPlannedRecipe(Recipe recipe, PlannedRecipe plannedRecipe) { - deleteOnePlannedMapping(recipe, plannedRecipe); // deletes mapping - int sizeOfInternalList = plannedRecipe.getRecipes().size(); - if (sizeOfInternalList == 1) { // recipe to be deleted is the last recipe - deletePlannedRecipe(plannedRecipe); + public void deleteOnePlan(Recipe recipe, PlannedDate plannedDate) { + if (plannedDate.isOneRecipe()) { // if one recipe is left, remove plannedDate + plannedDates.remove(plannedDate); } else { - plannedRecipe.getRecipes().remove(recipe); + plannedDates.remove(plannedDate); + plannedDate = plannedDate.deleteRecipe(recipe); + plannedDates.add(plannedDate); } + recipeMap.deleteOnePlannedRecipe(recipe, plannedDate); } /** - * Updates the recipe in the planned recipes from {@code target} to {@code editedRecipe}. + * Sets {@code target} to {@code editedRecipe} in the list and map. */ - public void setRecipeInPlans(Recipe target, Recipe editedRecipe) { - if (plannedRecipeMap.containsKey(target)) { - List plannedRecipesForRecipe = plannedRecipeMap.get(target); - plannedRecipeMap.remove(target); - for (PlannedRecipe plannedRecipe : plannedRecipesForRecipe) { - plannedRecipe.setRecipe(target, editedRecipe); + public void setRecipe(Recipe target, Recipe editedRecipe) { + if (recipeMap.contains(target)) { + List oldPlans = new ArrayList<>(recipeMap.getPlans(target)); + List newPlans = new ArrayList<>(); + for (PlannedDate plan : oldPlans) { + plannedDates.remove(plan); + PlannedDate newPlan = plan.setRecipe(target, editedRecipe); + newPlans.add(newPlan); + plannedDates.add(newPlan); } - plannedRecipeMap.put(editedRecipe, plannedRecipesForRecipe); + recipeMap.setRecipe(target, editedRecipe, newPlans); } } /** - * Deletes the {@code plannedRecipe} with all its internal recipes from the plannedRecipes list. - */ - private void deletePlannedRecipe(PlannedRecipe plannedRecipe) { - plannedRecipes.remove(plannedRecipe); - } - - // ===== Private mapping methods ===== - - /** - * Adds a {@code plannedRecipe} to the key {@code recipe} in the mapping of Recipe to PlannedRecipe. + * Favourites a recipe. */ - private void addPlannedMapping(Recipe recipe, PlannedRecipe plannedRecipe) { - if (plannedRecipeMap.containsKey(recipe)) { - plannedRecipeMap.get(recipe).add(plannedRecipe); - } else { - List plannedRecipes = new ArrayList<>(); - plannedRecipes.add(plannedRecipe); - plannedRecipeMap.put(recipe, plannedRecipes); - } - } - - /** - * Adds {@code plannedRecipe} to all the keys in {@code recipes} in the mapping of Recipe to PlannedRecipe. - */ - private void addAllPlannedMapping(List recipes, PlannedRecipe plannedRecipe) { - for (Recipe recipe : recipes) { - if (plannedRecipeMap.containsKey(recipe)) { - plannedRecipeMap.get(recipe).add(plannedRecipe); - } else { - List plannedRecipes = new ArrayList<>(); - plannedRecipes.add(plannedRecipe); - plannedRecipeMap.put(recipe, plannedRecipes); - } - } + public void favouriteRecipe(Recipe toFavourite) { + Recipe newRecipe = new Recipe(toFavourite.getName(), toFavourite.getTime(), toFavourite.getGrains(), + toFavourite.getVegetables(), toFavourite.getProteins(), toFavourite.getFruits(), + toFavourite.getOthers(), toFavourite.getSteps(), toFavourite.getGoals(), true); + setRecipe(toFavourite, newRecipe); } /** - * Deletes one {@code plannedRecipe} in the list of planned recipes at the {@code recipe} key. + * Unfavourites a recipe. */ - private void deleteOnePlannedMapping(Recipe recipe, PlannedRecipe plannedRecipe) { - plannedRecipeMap.get(recipe).remove(plannedRecipe); + public void unfavouriteRecipe(Recipe toUnfavourite) { + Recipe newRecipe = new Recipe(toUnfavourite.getName(), toUnfavourite.getTime(), toUnfavourite.getGrains(), + toUnfavourite.getVegetables(), toUnfavourite.getProteins(), toUnfavourite.getFruits(), + toUnfavourite.getOthers(), toUnfavourite.getSteps(), toUnfavourite.getGoals(), false); + setRecipe(toUnfavourite, newRecipe); } // ===== Util methods ===== @Override - public ObservableList getPlannedList() { - return plannedRecipes.asUnmodifiableObservableList(); + public ObservableList getPlannedList() { + return plannedDates.asUnmodifiableObservableList(); } @Override - public Map> getPlannedRecipeMap() { - return plannedRecipeMap; + public PlannedRecipeMap getPlannedMap() { + return recipeMap; } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof PlannedBook // instanceof handles nulls - && plannedRecipes.equals(((PlannedBook) other).plannedRecipes)); + && plannedDates.equals(((PlannedBook) other).plannedDates)); } @Override public int hashCode() { - return plannedRecipes.hashCode(); + return plannedDates.hashCode(); } } diff --git a/src/main/java/seedu/recipe/model/plan/PlannedRecipe.java b/src/main/java/seedu/recipe/model/plan/PlannedDate.java similarity index 50% rename from src/main/java/seedu/recipe/model/plan/PlannedRecipe.java rename to src/main/java/seedu/recipe/model/plan/PlannedDate.java index a85c575e422..2a42943b0ec 100644 --- a/src/main/java/seedu/recipe/model/plan/PlannedRecipe.java +++ b/src/main/java/seedu/recipe/model/plan/PlannedDate.java @@ -1,5 +1,6 @@ package seedu.recipe.model.plan; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -14,12 +15,12 @@ * Stores the list of all recipes planned on a date. * Guarantees: details are present and not null, field values are validated, immutable. */ -public class PlannedRecipe implements Comparable { +public class PlannedDate implements Comparable { private List recipes; private Date date; - public PlannedRecipe(List recipes, Date date) { + public PlannedDate(List recipes, Date date) { this.recipes = recipes; this.date = date; } @@ -32,38 +33,55 @@ public void setRecipes(List recipes) { } /** - * Replaces the {@code target} Recipe with the {@code newRecipe} recipe in the planned recipe's list of recipes. + * Replaces the {@code target} Recipe with the {@code newRecipe} recipe in the planned recipe's list of recipes + * and returns a new PlannedDate. */ - public void setRecipe(Recipe target, Recipe newRecipe) { - int index = recipes.indexOf(target); - recipes.set(index, newRecipe); + public PlannedDate setRecipe(Recipe target, Recipe newRecipe) { + List newRecipes = new ArrayList<>(recipes); + int index = newRecipes.indexOf(target); + newRecipes.set(index, newRecipe); + return new PlannedDate(newRecipes, date); } /** - * Adds the recipes in {@code plannedRecipe} to the recipes in this Planned Recipe object and returns a new - * Planned Recipe object. + * Concatenates all recipes planned on the same day and returns a new PlannedDate. */ - public PlannedRecipe allPlannedRecipesOnOneDay(PlannedRecipe plannedRecipe) { - if (recipes.contains(plannedRecipe)) { + public PlannedDate addRecipes(PlannedDate plannedDate) { + if (recipes.contains(plannedDate)) { throw new DuplicateRecipeException(); } - List newRecipes = plannedRecipe.getRecipes(); - newRecipes.addAll(recipes); - return new PlannedRecipe(newRecipes, date); + List newRecipes = new ArrayList<>(recipes); + newRecipes.addAll(plannedDate.getRecipes()); + return new PlannedDate(newRecipes, date); } /** - * Returns true if {@code plannedRecipe} is planned on the same day and all its recipes - * can be found in the current PlannedRecipe's list, or if duplicate recipes are found in {@code plannedRecipe}. + * Deletes {@code recipe} from the list and returns a new PlannedDate. */ - public boolean hasSameRecipeInPlan(PlannedRecipe plannedRecipe) { - if (plannedRecipe.recipesAreUnique()) { - return date.equals(plannedRecipe.date) && recipes.containsAll(plannedRecipe.getRecipes()); + public PlannedDate deleteRecipe(Recipe recipe) { + List newRecipes = new ArrayList<>(recipes); + newRecipes.remove(recipe); + return new PlannedDate(newRecipes, date); + } + + /** + * Returns true if the size of recipes is 1. + */ + public boolean isOneRecipe() { + return recipes.size() == 1; + } + + /** + * Returns true if {@code plannedDate} is planned on the same day and all its recipes + * can be found in the current PlannedDate's list, or if duplicate recipes are found in {@code plannedDate}. + */ + public boolean hasSameRecipeInPlan(PlannedDate plannedDate) { + if (plannedDate.recipesAreUnique()) { + return date.equals(plannedDate.date) && recipes.containsAll(plannedDate.getRecipes()); } return true; } - /** * Returns true if it is planned on {@code onDate}. */ @@ -92,6 +110,10 @@ public boolean isWithinRange(Date start, Date end) { return date.isWithinRange(start, end); } + public boolean hasSameDate(PlannedDate other) { + return date.equals(other.date); + } + public Date getDate() { return date; } @@ -101,7 +123,7 @@ public List getRecipes() { } @Override - public int compareTo(PlannedRecipe other) { + public int compareTo(PlannedDate other) { return date.compareTo(other.date); } @@ -113,9 +135,14 @@ public int hashCode() { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof PlannedRecipe // instanceof handles nulls - && date.equals(((PlannedRecipe) other).date) // state check - && recipes.equals(((PlannedRecipe) other).recipes)); + || (other instanceof PlannedDate // instanceof handles nulls + && date.equals(((PlannedDate) other).date) // state check + && recipes.equals(((PlannedDate) other).recipes)); + } + + @Override + public String toString() { + return date.toString() + "\n" + recipes.toString(); } } diff --git a/src/main/java/seedu/recipe/model/plan/PlannedRecipeMap.java b/src/main/java/seedu/recipe/model/plan/PlannedRecipeMap.java new file mode 100644 index 00000000000..4eb14dce008 --- /dev/null +++ b/src/main/java/seedu/recipe/model/plan/PlannedRecipeMap.java @@ -0,0 +1,115 @@ +package seedu.recipe.model.plan; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import seedu.recipe.model.recipe.Recipe; + +/** + * A map of recipe to list-of-planned-recipes that contains mappings to ease the updating of planned recipes when + * the original recipes are changed. + */ +public class PlannedRecipeMap { + + private Map> internalMap; + + public PlannedRecipeMap() { + internalMap = new HashMap<>(); + } + + /** + * Replaces the contents of the map with {@code plannedRecipeMap}. + */ + public void setPlannedRecipeMap(PlannedRecipeMap plannedRecipeMap) { + internalMap.clear(); + internalMap.putAll(plannedRecipeMap.getInternalMap()); + } + + /** + * Adds the {@code plannedDate} to the list of planned recipes at {@code recipe}. + */ + public void addOnePlannedRecipe(Recipe recipe, PlannedDate plannedDate) { + if (internalMap.containsKey(recipe)) { + internalMap.get(recipe).add(plannedDate); + } else { + List allPlans = new ArrayList<>(); + allPlans.add(plannedDate); + internalMap.put(recipe, allPlans); + } + } + + /** + * Adds all {@code plannedDates} to the {@code recipe} key. + */ + public void addAllPlannedRecipes(Recipe recipe, List plannedDates) { + if (internalMap.containsKey(recipe)) { + internalMap.get(recipe).addAll(plannedDates); + } else { + internalMap.put(recipe, plannedDates); + } + } + + /** + * Adds the {@code plannedDate} to all {@code recipes} keys + */ + public void addAllRecipesToPlan(List recipes, PlannedDate plannedDate) { + recipes.stream().forEach(recipe -> addOnePlannedRecipe(recipe, plannedDate)); + } + + + /** + * Deletes the {@code plannedDate} from the list at the {@code recipe} key. + */ + public void deleteOnePlannedRecipe(Recipe recipe, PlannedDate plannedDate) { + List allPlans = internalMap.get(recipe); + if (allPlans.size() == 1) { // only one plan in this list + internalMap.remove(recipe); + } else { + allPlans.remove(plannedDate); + } + } + + /** + * Deletes the list of planned recipes at {@code recipe} key. + */ + public void deleteAllPlannedRecipes(Recipe recipe) { + internalMap.remove(recipe); + } + + /** + * Returns a new list of planned dates at the {@code recipe} key. + */ + public List getPlans(Recipe recipe) { + if (contains(recipe)) { + return new ArrayList<>(internalMap.get(recipe)); + } else { + return new ArrayList<>(); + } + } + + /** + * Removes the values at {@code target} key and places the updated values at the {@code editedRecipe} key. + */ + public void setRecipe(Recipe target, Recipe editedRecipe, List newPlans) { + if (internalMap.containsKey(target)) { + internalMap.remove(target); + internalMap.put(editedRecipe, newPlans); + } + } + + /** + * Returns true if map contains {@code key}. + */ + public boolean contains(Recipe key) { + return internalMap.containsKey(key); + } + + /** + * Returns a new copy of the recipe to list-of-planned-recipes mapping. + */ + private Map> getInternalMap() { + return new HashMap<>(internalMap); + } +} diff --git a/src/main/java/seedu/recipe/model/plan/PlannedRecipeOnDatePredicate.java b/src/main/java/seedu/recipe/model/plan/PlannedRecipeOnDatePredicate.java index 74ec56a19f7..76212e1a4d2 100644 --- a/src/main/java/seedu/recipe/model/plan/PlannedRecipeOnDatePredicate.java +++ b/src/main/java/seedu/recipe/model/plan/PlannedRecipeOnDatePredicate.java @@ -5,9 +5,9 @@ import seedu.recipe.model.Date; /** - * Tests that a PlannedRecipe PlannedDate is on the specified Date. + * Tests that a PlannedDate PlannedDate is on the specified Date. */ -public class PlannedRecipeOnDatePredicate implements Predicate { +public class PlannedRecipeOnDatePredicate implements Predicate { private Date onDate; @@ -16,7 +16,7 @@ public PlannedRecipeOnDatePredicate(Date onDate) { } @Override - public boolean test(PlannedRecipe recipe) { + public boolean test(PlannedDate recipe) { return recipe.isOnDate(onDate); } diff --git a/src/main/java/seedu/recipe/model/plan/PlannedRecipeWithinDateRangePredicate.java b/src/main/java/seedu/recipe/model/plan/PlannedRecipeWithinDateRangePredicate.java index 67d9e4104fc..6718fd650cb 100644 --- a/src/main/java/seedu/recipe/model/plan/PlannedRecipeWithinDateRangePredicate.java +++ b/src/main/java/seedu/recipe/model/plan/PlannedRecipeWithinDateRangePredicate.java @@ -5,10 +5,10 @@ import seedu.recipe.model.Date; /** - * Tests that a PlannedRecipe's PlannedDate falls within the specified Date range. + * Tests that a PlannedDate's PlannedDate falls within the specified Date range. * The start and end dates are non-inclusive. */ -public class PlannedRecipeWithinDateRangePredicate implements Predicate { +public class PlannedRecipeWithinDateRangePredicate implements Predicate { private Date start; private Date end; @@ -19,7 +19,7 @@ public PlannedRecipeWithinDateRangePredicate(Date start, Date end) { } @Override - public boolean test(PlannedRecipe recipe) { + public boolean test(PlannedDate recipe) { return recipe.isWithinRange(start, end); } } diff --git a/src/main/java/seedu/recipe/model/plan/UniquePlannedList.java b/src/main/java/seedu/recipe/model/plan/UniquePlannedList.java index 11412f30514..a42e313ec85 100644 --- a/src/main/java/seedu/recipe/model/plan/UniquePlannedList.java +++ b/src/main/java/seedu/recipe/model/plan/UniquePlannedList.java @@ -3,85 +3,82 @@ import static java.util.Objects.requireNonNull; import static seedu.recipe.commons.util.CollectionUtil.requireAllNonNull; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.recipe.model.Date; /** - * A list of planned recipes that enforces uniqueness between its elements and does not allow nulls. - * Ensures that at any point in time, the items in the list of planned recipes each has a unique Date. - * This means that PlannedRecipe items with the same Date must not exist in the list. + * A list of planned dates that enforces uniqueness between its elements and does not allow nulls. + * Ensures that at any point in time, the items in the list of planned dates each has a unique Date. + * This means that PlannedDate items with the same Date must not exist in the list. * - * A planned recipe is considered unique by comparing using {@code PlannedRecipe#equals(object)}. + * A planned date is considered unique by comparing using {@code PlannedDate#equals(object)}. * * Supports a minimal set of list operations. */ public class UniquePlannedList { - private ObservableList observableList = FXCollections.observableArrayList(); - private ObservableList unmodifiableObservableList = FXCollections - .unmodifiableObservableList(observableList); - - // todo: check duplicate if it is, throw exception. + private ObservableList internalList = FXCollections.observableArrayList(); + private ObservableList unmodifiableObservableList = FXCollections + .unmodifiableObservableList(internalList); /** - * Replaces the contents of this map with {@code plannedRecipes}. - * {@code plannedRecipes} must not contain duplicate recipes on the same date. + * Replaces the contents of this map with {@code plannedDates}. + * {@code plannedDates} must not contain duplicate recipes on the same date. */ - public void setPlannedRecipes(ObservableList plannedRecipes) { - requireAllNonNull(plannedRecipes); - if (!plannedRecipesAreUnique(plannedRecipes)) { - throw new DuplicatePlannedRecipeException(); - } - - if (!recipesAreUnique(plannedRecipes)) { + public void setPlannedDates(ObservableList plannedDates) { + requireAllNonNull(plannedDates); + if (!plannedDatesAreUnique(plannedDates)) { throw new DuplicatePlannedRecipeException(); } - observableList.clear(); - observableList.setAll(plannedRecipes); + internalList.clear(); + internalList.setAll(plannedDates); } /** - * Adds a planned recipe to the plannedRecipes list. + * Adds {@code plannedDate} to the list. */ - public void add(PlannedRecipe plannedRecipe) { - requireNonNull(plannedRecipe); - if (observableList.contains(plannedRecipe) || hasPlannedRecipe(plannedRecipe)) { + public void add(PlannedDate plannedDate) { + requireNonNull(plannedDate); + if (internalList.contains(plannedDate) || hasPlannedDate(plannedDate)) { throw new DuplicatePlannedRecipeException(); } - - int indexOfSameDate = indexOfPlannedRecipeWithSameDate(plannedRecipe); - if (indexOfSameDate == -1) { - observableList.add(plannedRecipe); + Optional optionalSameDate = withSameDate(plannedDate); + if (optionalSameDate.isEmpty()) { + internalList.add(plannedDate); // planned on a new day } else { - PlannedRecipe currentPlannedRecipe = observableList.get(indexOfSameDate) - .allPlannedRecipesOnOneDay(plannedRecipe); - observableList.set(indexOfSameDate, currentPlannedRecipe); + PlannedDate sameDate = optionalSameDate.get(); + internalList.remove(sameDate); + sameDate = sameDate.addRecipes(plannedDate); + internalList.add(sameDate); + System.out.println("reached, internal list: " + internalList.size()); } } /** - * Removes the planned recipe from the plannedRecipes list. + * Removes {@code plannedDate} from the list. */ - public void remove(PlannedRecipe plannedRecipe) { - requireNonNull(plannedRecipe); - if (!observableList.remove(plannedRecipe)) { + public void remove(PlannedDate plannedDate) { + requireNonNull(plannedDate); + System.out.println("removing + " + plannedDate); //todo remove + if (!internalList.remove(plannedDate)) { throw new PlannedRecipeNotFoundException(); } } /** - * Returns true if the list contains the same recipes at the planned date from {@code otherPlannedRecipe}. + * Returns true if the list contains the same recipes at the planned date from {@code otherPlannedDate}. */ - public boolean hasPlannedRecipe(PlannedRecipe otherPlannedRecipe) { - if (observableList.contains(otherPlannedRecipe)) { + public boolean hasPlannedDate(PlannedDate otherPlannedDate) { + if (internalList.contains(otherPlannedDate)) { return true; } - for (PlannedRecipe plannedRecipe : observableList) { - if (plannedRecipe.hasSameRecipeInPlan(otherPlannedRecipe)) { + for (PlannedDate plannedDate : internalList) { + if (plannedDate.hasSameRecipeInPlan(otherPlannedDate)) { return true; } } @@ -89,41 +86,31 @@ public boolean hasPlannedRecipe(PlannedRecipe otherPlannedRecipe) { } /** - * Returns the index of a planned recipe with the same date as {@code otherPlannedRecipe}. + * Returns an optional planned date with the same date as {@code otherPlannedDate}. + * Returns optional empty if no such planned date exists. */ - private int indexOfPlannedRecipeWithSameDate(PlannedRecipe otherPlannedRecipe) { - Date otherDate = otherPlannedRecipe.getDate(); - for (int i = 0; i < observableList.size(); i++) { - Date currentDate = observableList.get(i).getDate(); - if (currentDate.equals(otherDate)) { - return i; - } - } - return -1; + private Optional withSameDate(PlannedDate otherPlannedDate) { + List copyOfInternal = new ArrayList<>(internalList); + return copyOfInternal.stream().filter(plan -> plan.hasSameDate(otherPlannedDate)).findFirst(); } /** - * Returns true if {@code plannedRecipes} contains only unique planned recipes. + * Returns true if {@code plannedDates} contains only unique planned dates. */ - private boolean plannedRecipesAreUnique(List plannedRecipes) { - for (int i = 0; i < plannedRecipes.size() - 1; i++) { - for (int j = i + 1; j < plannedRecipes.size(); j++) { - if (plannedRecipes.get(i).equals(plannedRecipes.get(j))) { + private boolean plannedDatesAreUnique(List plannedDates) { + for (int i = 0; i < plannedDates.size() - 1; i++) { + if (!plannedDates.get(i).recipesAreUnique()) { + return false; // checks if the recipes in each plannedDate is unique + } + for (int j = i + 1; j < plannedDates.size(); j++) { + if (plannedDates.get(i).equals(plannedDates.get(j))) { return false; } } } - return true; - } - - /** - * Returns true if all recipes in {@code plannedRecipes} contains only unique recipes. - */ - public boolean recipesAreUnique(List plannedRecipes) { - for (PlannedRecipe plannedRecipe : plannedRecipes) { - if (!plannedRecipe.recipesAreUnique()) { - return false; - } + int lastItem = plannedDates.size() - 1; + if (lastItem > 0 && !plannedDates.get(plannedDates.size() - 1).recipesAreUnique()) { + return false; // checks if the recipes in the last plannedDate is unique } return true; } @@ -131,21 +118,24 @@ public boolean recipesAreUnique(List plannedRecipes) { /** * Sorts the list then returns it as an unmodifiable {@code ObservableList}. */ - public ObservableList asUnmodifiableObservableList() { - FXCollections.sort(observableList); + public ObservableList asUnmodifiableObservableList() { + FXCollections.sort(internalList); return unmodifiableObservableList; } @Override public int hashCode() { - return observableList.hashCode(); + return internalList.hashCode(); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof UniquePlannedList // instanceof handles nulls - && observableList.equals(((UniquePlannedList) other).observableList)); + && internalList.equals(((UniquePlannedList) other).internalList)); } + public String size() { + return internalList.size() + ""; + } } diff --git a/src/main/java/seedu/recipe/storage/JsonAdaptedDate.java b/src/main/java/seedu/recipe/storage/JsonAdaptedDate.java new file mode 100644 index 00000000000..5e1270953d0 --- /dev/null +++ b/src/main/java/seedu/recipe/storage/JsonAdaptedDate.java @@ -0,0 +1,48 @@ +package seedu.recipe.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import seedu.recipe.commons.exceptions.IllegalValueException; +import seedu.recipe.model.Date; + +/** + * Jackson-friendly version of {@link Date}. + */ +public class JsonAdaptedDate { + + private final String date; + + /** + * Constructs a {@code JsonAdaptedDate} with the given {@code plannedDate}. + */ + @JsonCreator + public JsonAdaptedDate(String plannedDate) { + date = plannedDate; + } + + /** + * Converts a given {@code plannedDate} into this class for Jackson use. + */ + public JsonAdaptedDate(Date plannedDate) { + date = plannedDate.toStringForJson(); + } + + @JsonValue + public String getDate() { + return date; + } + + /** + * Converts this Jackson-friendly adapted plannedDate object into the model's {@code Date} object. + * @return Date object that the adapted plannedDate was converted into. + * @throws IllegalValueException if there were any data constraints violated in the adapted Date. + */ + public Date toModelType() throws IllegalValueException { + if (!Date.isValidDate(date)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + + return new Date(date); + } +} diff --git a/src/main/java/seedu/recipe/storage/Storage.java b/src/main/java/seedu/recipe/storage/Storage.java index 226e9b1e19e..32c12d2f369 100644 --- a/src/main/java/seedu/recipe/storage/Storage.java +++ b/src/main/java/seedu/recipe/storage/Storage.java @@ -6,10 +6,10 @@ import seedu.recipe.commons.exceptions.DataConversionException; import seedu.recipe.model.ReadOnlyCookedRecordBook; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.ReadOnlyUserPrefs; import seedu.recipe.model.UserPrefs; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; import seedu.recipe.storage.plan.PlannedBookStorage; /** diff --git a/src/main/java/seedu/recipe/storage/StorageManager.java b/src/main/java/seedu/recipe/storage/StorageManager.java index 456cddec472..a45208f0ac7 100644 --- a/src/main/java/seedu/recipe/storage/StorageManager.java +++ b/src/main/java/seedu/recipe/storage/StorageManager.java @@ -8,10 +8,10 @@ import seedu.recipe.commons.core.LogsCenter; import seedu.recipe.commons.exceptions.DataConversionException; import seedu.recipe.model.ReadOnlyCookedRecordBook; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.ReadOnlyUserPrefs; import seedu.recipe.model.UserPrefs; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; import seedu.recipe.storage.plan.PlannedBookStorage; /** diff --git a/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedDate.java b/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedDate.java index 34827a00829..eda74b5657a 100644 --- a/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedDate.java +++ b/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedDate.java @@ -1,48 +1,69 @@ package seedu.recipe.storage.plan; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonProperty; import seedu.recipe.commons.exceptions.IllegalValueException; import seedu.recipe.model.Date; +import seedu.recipe.model.plan.PlannedDate; +import seedu.recipe.model.recipe.Recipe; +import seedu.recipe.storage.JsonAdaptedDate; +import seedu.recipe.storage.JsonAdaptedRecipe; /** - * Jackson-friendly version of {@link Date}. + * Jackson-friendly version of {@link PlannedDate}. */ public class JsonAdaptedPlannedDate { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Planned recipe's %s field is missing!"; - private final String date; + private final List recipes = new ArrayList<>(); + private final JsonAdaptedDate date; /** - * Constructs a {@code JsonAdaptedPlannedDate} with the given {@code plannedDate}. + * Constructs a {@code JsonAdaptedPlannedDate} with the given {@code PlannedDate}. */ @JsonCreator - public JsonAdaptedPlannedDate(String plannedDate) { - date = plannedDate; + public JsonAdaptedPlannedDate(@JsonProperty("recipes") List recipes, + @JsonProperty("date") JsonAdaptedDate date) { + if (recipes != null) { + this.recipes.addAll(recipes); + } + this.date = date; } /** * Converts a given {@code plannedDate} into this class for Jackson use. */ - public JsonAdaptedPlannedDate(Date plannedDate) { - date = plannedDate.toStringForJson(); - } - - @JsonValue - public String getDate() { - return date; + public JsonAdaptedPlannedDate(PlannedDate plannedDate) { + recipes.addAll(plannedDate.getRecipes() + .stream() + .map(JsonAdaptedRecipe::new) + .collect(Collectors.toList())); + date = new JsonAdaptedDate(plannedDate.getDate().toStringForJson()); } /** - * Converts this Jackson-friendly adapted plannedDate object into the model's {@code Date} object. - * @return Date object that the adapted plannedDate was converted into. - * @throws IllegalValueException if there were any data constraints violated in the adapted Date. + * Converts this Jackson-friendly adapted PlannedDate object into the model's {@code PlannedDate} object. + * @return PlannedDate object that the adapted PlannedDate was converted into. + * @throws IllegalValueException if there were any data constraints violated in the adapted PlannedDate. */ - public Date toModelType() throws IllegalValueException { - if (!Date.isValidDate(date)) { - throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + public PlannedDate toModelType() throws IllegalValueException { + + final List modelRecipe = new ArrayList<>(); + for (JsonAdaptedRecipe recipe : recipes) { + modelRecipe.add(recipe.toModelType()); + } + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); } + final Date modelPlannedDate = date.toModelType(); - return new Date(date); + return new PlannedDate(modelRecipe, modelPlannedDate); } + + } diff --git a/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedRecipe.java b/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedRecipe.java deleted file mode 100644 index d72092ed55e..00000000000 --- a/src/main/java/seedu/recipe/storage/plan/JsonAdaptedPlannedRecipe.java +++ /dev/null @@ -1,68 +0,0 @@ -package seedu.recipe.storage.plan; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.recipe.commons.exceptions.IllegalValueException; -import seedu.recipe.model.Date; -import seedu.recipe.model.plan.PlannedRecipe; -import seedu.recipe.model.recipe.Recipe; -import seedu.recipe.storage.JsonAdaptedRecipe; - -/** - * Jackson-friendly version of {@link PlannedRecipe}. - */ -public class JsonAdaptedPlannedRecipe { - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Planned recipe's %s field is missing!"; - - private final List recipes = new ArrayList<>(); - private final JsonAdaptedPlannedDate date; - - /** - * Constructs a {@code JsonAdaptedPlannedRecipe} with the given {@code PlannedRecipe}. - */ - @JsonCreator - public JsonAdaptedPlannedRecipe(@JsonProperty("recipes") List recipes, - @JsonProperty("date") JsonAdaptedPlannedDate date) { - if (recipes != null) { - this.recipes.addAll(recipes); - } - this.date = date; - } - - /** - * Converts a given {@code plannedRecipe} into this class for Jackson use. - */ - public JsonAdaptedPlannedRecipe(PlannedRecipe plannedRecipe) { - recipes.addAll(plannedRecipe.getRecipes() - .stream() - .map(JsonAdaptedRecipe::new) - .collect(Collectors.toList())); - date = new JsonAdaptedPlannedDate(plannedRecipe.getDate().toStringForJson()); - } - - /** - * Converts this Jackson-friendly adapted PlannedRecipe object into the model's {@code PlannedRecipe} object. - * @return PlannedRecipe object that the adapted PlannedRecipe was converted into. - * @throws IllegalValueException if there were any data constraints violated in the adapted PlannedRecipe. - */ - public PlannedRecipe toModelType() throws IllegalValueException { - - final List modelRecipe = new ArrayList<>(); - for (JsonAdaptedRecipe recipe : recipes) { - modelRecipe.add(recipe.toModelType()); - } - if (date == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); - } - final Date modelPlannedDate = date.toModelType(); - - return new PlannedRecipe(modelRecipe, modelPlannedDate); - } - - -} diff --git a/src/main/java/seedu/recipe/storage/plan/JsonPlannedBookStorage.java b/src/main/java/seedu/recipe/storage/plan/JsonPlannedBookStorage.java index 41d9a8ccd28..5564ffdef4d 100644 --- a/src/main/java/seedu/recipe/storage/plan/JsonPlannedBookStorage.java +++ b/src/main/java/seedu/recipe/storage/plan/JsonPlannedBookStorage.java @@ -12,7 +12,7 @@ import seedu.recipe.commons.exceptions.IllegalValueException; import seedu.recipe.commons.util.FileUtil; import seedu.recipe.commons.util.JsonUtil; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.ReadOnlyPlannedBook; /** * A class to access PlannedBook data stored as a json file on the hard disk. diff --git a/src/main/java/seedu/recipe/storage/plan/JsonSerializablePlannedBook.java b/src/main/java/seedu/recipe/storage/plan/JsonSerializablePlannedBook.java index ea60373f847..0a7789a8bf9 100644 --- a/src/main/java/seedu/recipe/storage/plan/JsonSerializablePlannedBook.java +++ b/src/main/java/seedu/recipe/storage/plan/JsonSerializablePlannedBook.java @@ -9,9 +9,9 @@ import com.fasterxml.jackson.annotation.JsonRootName; import seedu.recipe.commons.exceptions.IllegalValueException; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.plan.PlannedBook; -import seedu.recipe.model.plan.PlannedRecipe; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; /** @@ -23,13 +23,13 @@ class JsonSerializablePlannedBook { public static final String MESSAGE_DUPLICATE_PLANNED_RECIPE = "Planned recipes list contains duplicate" + "planned recipe(s)."; - private final List plannedRecipes = new ArrayList<>(); + private final List plannedRecipes = new ArrayList<>(); /** * Constructs a {@code JsonSerializablePlannedBook} with the given recipes. */ @JsonCreator - public JsonSerializablePlannedBook(@JsonProperty("plannedRecipes") List plannedRecipes) { + public JsonSerializablePlannedBook(@JsonProperty("plannedRecipes") List plannedRecipes) { this.plannedRecipes.addAll(plannedRecipes); } @@ -39,7 +39,7 @@ public JsonSerializablePlannedBook(@JsonProperty("plannedRecipes") List recipes = plannedRecipe.getRecipes(); - plannedBook.addPlanForAllRecipes(recipes, plannedRecipe); + List recipes = plannedDate.getRecipes(); + plannedBook.addAllRecipesToPlan(recipes, plannedDate); } return plannedBook; } diff --git a/src/main/java/seedu/recipe/storage/plan/PlannedBookStorage.java b/src/main/java/seedu/recipe/storage/plan/PlannedBookStorage.java index 772cf765b08..4afc8186777 100644 --- a/src/main/java/seedu/recipe/storage/plan/PlannedBookStorage.java +++ b/src/main/java/seedu/recipe/storage/plan/PlannedBookStorage.java @@ -5,7 +5,7 @@ import java.util.Optional; import seedu.recipe.commons.exceptions.DataConversionException; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.ReadOnlyPlannedBook; /** * Represents a storage for {@link seedu.recipe.model.plan.PlannedBook}. diff --git a/src/main/java/seedu/recipe/ui/HelpWindow.java b/src/main/java/seedu/recipe/ui/HelpWindow.java index f0ee832b964..c616f9cc145 100644 --- a/src/main/java/seedu/recipe/ui/HelpWindow.java +++ b/src/main/java/seedu/recipe/ui/HelpWindow.java @@ -15,7 +15,7 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay1920s2-cs2103t-t10-1.github.io/main/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/recipe/ui/MainWindow.java b/src/main/java/seedu/recipe/ui/MainWindow.java index 644b9db87f0..a66f91f64b1 100644 --- a/src/main/java/seedu/recipe/ui/MainWindow.java +++ b/src/main/java/seedu/recipe/ui/MainWindow.java @@ -18,7 +18,7 @@ import seedu.recipe.logic.commands.exceptions.CommandException; import seedu.recipe.logic.parser.exceptions.ParseException; import seedu.recipe.model.cooked.Record; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; import seedu.recipe.ui.tab.Tab; @@ -132,7 +132,7 @@ void fillInnerParts() { ObservableList recipeList = logic.getFilteredRecipeList(); recipeListPanel = new RecipeListPanel(recipeList); - ObservableList plannedList = logic.getFilteredPlannedList(); + ObservableList plannedList = logic.getFilteredPlannedList(); planningListPanel = new PlanningListPanel(plannedList); ObservableList cookedList = logic.getFilteredRecordList(); diff --git a/src/main/java/seedu/recipe/ui/PlanningListCard.java b/src/main/java/seedu/recipe/ui/PlanningListCard.java index a13b34416c8..c05a17b08f7 100644 --- a/src/main/java/seedu/recipe/ui/PlanningListCard.java +++ b/src/main/java/seedu/recipe/ui/PlanningListCard.java @@ -9,7 +9,7 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import seedu.recipe.model.Date; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; /** @@ -19,7 +19,7 @@ public class PlanningListCard extends UiPart { private static final String FXML = "PlanningListCard.fxml"; - public final PlannedRecipe plannedRecipeObject; + public final PlannedDate plannedDateObject; public final Date plannedDate; public final List recipes; @@ -34,13 +34,13 @@ public class PlanningListCard extends UiPart { @FXML private VBox recipesBox; - public PlanningListCard(PlannedRecipe plannedRecipe, int displayedIndex) throws IOException { + public PlanningListCard(PlannedDate plannedDate, int displayedIndex) throws IOException { super(FXML); - this.plannedRecipeObject = plannedRecipe; - this.plannedDate = plannedRecipe.getDate(); - date.setText(plannedDate.toString()); + this.plannedDateObject = plannedDate; + this.plannedDate = plannedDate.getDate(); + date.setText(this.plannedDate.toString()); - this.recipes = plannedRecipe.getRecipes(); + this.recipes = plannedDate.getRecipes(); for (int i = 0; i < recipes.size(); i++) { Recipe recipe = recipes.get(i); diff --git a/src/main/java/seedu/recipe/ui/PlanningListPanel.java b/src/main/java/seedu/recipe/ui/PlanningListPanel.java index 0f3e1982476..fa25ca94105 100644 --- a/src/main/java/seedu/recipe/ui/PlanningListPanel.java +++ b/src/main/java/seedu/recipe/ui/PlanningListPanel.java @@ -14,7 +14,7 @@ import seedu.recipe.commons.core.LogsCenter; import seedu.recipe.commons.util.StringUtil; import seedu.recipe.model.Date; -import seedu.recipe.model.plan.PlannedRecipe; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.plan.UniquePlannedList; /** @@ -35,7 +35,7 @@ public class PlanningListPanel extends UiPart { @FXML - private ListView planningListView; + private ListView planningListView; @FXML private BorderPane borderPane; @@ -46,7 +46,7 @@ public class PlanningListPanel extends UiPart { @FXML private Label dayHeader; - public PlanningListPanel(ObservableList plannedRecipes) { + public PlanningListPanel(ObservableList plannedDates) { super(FXML); Date today = Date.today(); monthHeader.setText(today.getMonthName()); @@ -55,24 +55,24 @@ public PlanningListPanel(ObservableList plannedRecipes) { dayHeader.setText(today.getDayOfWeek() + ": " + today.toString()); dayHeader.setStyle(weekStyleHeader); - planningListView.setItems(plannedRecipes); + planningListView.setItems(plannedDates); planningListView.setCellFactory(planningListView -> new PlanningListViewCell()); } /** - * Custom {@code ListCell} that displays the graphics of a {@code PlannedRecipe} using a {@code PlanningListCard}. + * Custom {@code ListCell} that displays the graphics of a {@code PlannedDate} using a {@code PlanningListCard}. */ - class PlanningListViewCell extends ListCell { + class PlanningListViewCell extends ListCell { @Override - protected void updateItem(PlannedRecipe plannedRecipe, boolean empty) { - super.updateItem(plannedRecipe, empty); - if (empty || plannedRecipe == null) { + protected void updateItem(PlannedDate plannedDate, boolean empty) { + super.updateItem(plannedDate, empty); + if (empty || plannedDate == null) { setGraphic(null); setText(null); } else { try { - setGraphic(new PlanningListCard(plannedRecipe, getIndex() + 1).getRoot()); + setGraphic(new PlanningListCard(plannedDate, getIndex() + 1).getRoot()); } catch (IOException e) { logger.warning("Failed to favourites icon : " + StringUtil.getDetails(e)); } diff --git a/src/test/java/seedu/recipe/logic/commands/AddCommandTest.java b/src/test/java/seedu/recipe/logic/commands/AddCommandTest.java index b4d6c338090..c7cbb5e1dd9 100644 --- a/src/test/java/seedu/recipe/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/recipe/logic/commands/AddCommandTest.java @@ -19,12 +19,12 @@ import seedu.recipe.logic.commands.exceptions.CommandException; import seedu.recipe.model.Model; import seedu.recipe.model.ReadOnlyCookedRecordBook; +import seedu.recipe.model.ReadOnlyPlannedBook; import seedu.recipe.model.ReadOnlyRecipeBook; import seedu.recipe.model.ReadOnlyUserPrefs; import seedu.recipe.model.RecipeBook; import seedu.recipe.model.cooked.Record; -import seedu.recipe.model.plan.PlannedRecipe; -import seedu.recipe.model.plan.ReadOnlyPlannedBook; +import seedu.recipe.model.plan.PlannedDate; import seedu.recipe.model.recipe.Recipe; import seedu.recipe.testutil.RecipeBuilder; @@ -199,22 +199,22 @@ public void setPlannedBook(ReadOnlyPlannedBook plannedBook) { } @Override - public void addPlanForOneRecipe(Recipe recipe, PlannedRecipe plannedRecipe) { + public void addOnePlan(Recipe recipe, PlannedDate plannedDate) { throw new AssertionError("This method should not be called."); } @Override - public void addPlanForAllRecipes(List recipes, PlannedRecipe plannedRecipe) { + public void addAllRecipesToPlan(List recipes, PlannedDate plannedDate) { throw new AssertionError("This method should not be called."); } @Override - public void deleteRecipeFromOnePlan(Recipe recipe, PlannedRecipe plannedRecipe) { + public void deleteOnePlan(Recipe recipe, PlannedDate plannedDate) { throw new AssertionError("This method should not be called."); } @Override - public void deleteAllPlansFor(Recipe recipe) { + public void deleteAllRecipePlans(Recipe recipe) { throw new AssertionError("This method should not be called."); } @@ -229,7 +229,7 @@ public ObservableList getFilteredRecordList() { } @Override - public ObservableList getFilteredPlannedList() { + public ObservableList getFilteredPlannedList() { throw new AssertionError("This method should not be called."); } @@ -244,7 +244,7 @@ public boolean hasRecord(Record record) { } @Override - public void updateFilteredPlannedList(Predicate predicate) { + public void updateFilteredPlannedList(Predicate predicate) { throw new AssertionError("This method should not be called."); } diff --git a/src/test/java/seedu/recipe/logic/commands/plan/PlanCommandTest.java b/src/test/java/seedu/recipe/logic/commands/plan/PlanCommandTest.java new file mode 100644 index 00000000000..ea1c30e5b66 --- /dev/null +++ b/src/test/java/seedu/recipe/logic/commands/plan/PlanCommandTest.java @@ -0,0 +1,33 @@ +package seedu.recipe.logic.commands.plan; + +import static seedu.recipe.testutil.Assert.assertThrows; +import static seedu.recipe.testutil.TypicalDates.DATE_IN_FUTURE; +import static seedu.recipe.testutil.TypicalIndexes.INDEX_FIRST_RECIPE; +import static seedu.recipe.testutil.TypicalRecipes.getTypicalRecipeBook; +import static seedu.recipe.testutil.TypicalRecords.getTypicalRecordBook; + +import org.junit.jupiter.api.Test; + +import seedu.recipe.model.Model; +import seedu.recipe.model.ModelManager; +import seedu.recipe.model.UserPrefs; +import seedu.recipe.model.plan.PlannedBook; + +class PlanCommandTest { + + private Model model = new ModelManager(getTypicalRecipeBook(), new UserPrefs(), + getTypicalRecordBook(), new PlannedBook()); + + @Test + public void constructor_null_throwsNullPointerException() { + // null index, null date + assertThrows(NullPointerException.class, () -> new PlanCommand(null, null)); + + // null index, valid date + assertThrows(NullPointerException.class, () -> new PlanCommand(null, DATE_IN_FUTURE)); + + // valid index, null date + assertThrows(NullPointerException.class, () -> new PlanCommand(INDEX_FIRST_RECIPE, null)); + } + +} diff --git a/src/test/java/seedu/recipe/model/DateTest.java b/src/test/java/seedu/recipe/model/DateTest.java new file mode 100644 index 00000000000..8ca6cdfaf7f --- /dev/null +++ b/src/test/java/seedu/recipe/model/DateTest.java @@ -0,0 +1,73 @@ +package seedu.recipe.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.recipe.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +class DateTest { + + @Test + public void constructor_invalidDate_throwsIllegalArgumentException() { + String invalidBlankDate = ""; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidBlankDate)); + + String invalidCharacterDate = "2020!09/02"; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidCharacterDate)); + + String invalidFormatDate = "02/09/2020"; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidFormatDate)); + + String invalidMonthDate = "2020/35/01"; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidMonthDate)); + + String invalidDayDate = "2020/01/32"; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidDayDate)); + } + + @Test + public void isValidDate() { + // null date + assertThrows(NullPointerException.class, () -> Date.isValidDate(null)); + + // invalid date + assertFalse(Date.isValidDate("")); // empty string + assertFalse(Date.isValidDate(" ")); // only spaces + assertFalse(Date.isValidDate("!")); // only non-numeric or - character + assertFalse(Date.isValidDate("20200202")); // not in yyyy-mm-dd format + assertFalse(Date.isValidDate("2020-13-02")); // invalid month + assertFalse(Date.isValidDate("2020-01-32")); // invalid day + + // valid date + assertTrue(Date.isValidDate("1000-02-02")); // date in the past + assertTrue(Date.isValidDate("2020-02-02")); // date in current year + assertTrue(Date.isValidDate("5000-02-02")); // date in the future + } + + @Test + public void isDateInFuture() { + Date olderDate = new Date("1000-01-01"); // date about 1000 years in the past + assertFalse(olderDate.isDateInFuture()); + + Date newerDate = new Date("5000-01-01"); // date about 3000 years in the future + assertTrue(newerDate.isDateInFuture()); + } + + @Test + public void isWithinRange() { + Date pastDate = new Date("1000-01-01"); // date about 1000 years in the past + Date recentDate = new Date("2000-01-01"); // date in this millennium + Date futureDate = new Date("5000-01-01"); // date about 3000 years in the future + + // recent date is within start and end date + assertTrue(recentDate.isWithinRange(pastDate, futureDate)); + + // start date is later than end date + assertFalse(recentDate.isWithinRange(futureDate, pastDate)); + + // start and end date are the same + assertFalse(recentDate.isWithinRange(pastDate, pastDate)); + assertFalse(recentDate.isWithinRange(futureDate, futureDate)); + } +} diff --git a/src/test/java/seedu/recipe/model/recipe/NameTest.java b/src/test/java/seedu/recipe/model/recipe/NameTest.java index dcdda8dee46..7c39480a947 100644 --- a/src/test/java/seedu/recipe/model/recipe/NameTest.java +++ b/src/test/java/seedu/recipe/model/recipe/NameTest.java @@ -27,7 +27,7 @@ public void isValidName() { // invalid name assertFalse(Name.isValidName("")); // empty string assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters + assertFalse(Name.isValidName("^")); // only non-alphanumeric chars except permitted special characters. assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters // valid name diff --git a/src/test/java/seedu/recipe/testutil/TypicalDates.java b/src/test/java/seedu/recipe/testutil/TypicalDates.java new file mode 100644 index 00000000000..43791ec3453 --- /dev/null +++ b/src/test/java/seedu/recipe/testutil/TypicalDates.java @@ -0,0 +1,12 @@ +package seedu.recipe.testutil; + +import seedu.recipe.model.Date; + +/** + * A utility class containing a list of {@code Date} objects to be used in tests. + */ +public class TypicalDates { + public static final Date DATE_IN_PAST = new Date("2000-05-05"); + public static final Date DATE_IN_FUTURE = new Date("3000-05-05"); + public static final Date DATE_TODAY = Date.today(); +}