Skip to content

Commit

Permalink
Merge pull request #450 from yixiann/fix-dates
Browse files Browse the repository at this point in the history
Ensure we cannot create an item with bought date after expiry date
  • Loading branch information
RichDom2185 authored Nov 2, 2022
2 parents 22a3f4f + d8dd2f0 commit d4acccd
Show file tree
Hide file tree
Showing 13 changed files with 314 additions and 287 deletions.
7 changes: 4 additions & 3 deletions docs/_ug/commands/ItemCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Example of an [Item](#item):
```info
* All fields apart from `ITEM_NAME` are optional.
* The `BOUGHT_DATE` ideally should not be after the `EXPIRY_DATE` but we will allow that.
* The `BOUGHT_DATE` should not be after the `EXPIRY_DATE`.
* The format for `BOUGHT_DATE` and `EXPIRY_DATE` should follow: "dd-mm-yyyy".
* dd: Day of the month. For example, "10" would represent the 10th day of the month.
* mm: Month of the year, ranging from 1 to 12. This represents the months from January to December. For example, "01" would represent January.
* yyyy: The current year. For example, "2019" would represent the year 2019.
* The value of `BOUGHT_DATE`, `EXPIRY_DATE` will be `Not Set` if it is not provided.
* The default values for `QUANTITY` and `PRICE` is `0`.
* The default values for `UNIT` is blank.
* The value of `BOUGHT_DATE`, `EXPIRY_DATE` will be `Not Set` if not provided.
* The value of `REMARKS` will be `-` if not provided.
* The value of `REMARKS` will be `-` if is it not provided.
* `PRICE` do not require you to include the currency. Only include the value.
* You cannot create an item with a tag immediately.
* If two or more of the same parameters are provided, the last parameter will be taken.
Expand Down Expand Up @@ -258,6 +258,7 @@ Tags: {vegetables}
```info
* All fields are optional. However, you need to include at least one parameter.
* The `BOUGHT_DATE` should not be after the `EXPIRY_DATE`.
* The format for `BOUGHT_DATE` and `EXPIRY_DATE` should follow: "dd-mm-yyyy".
* dd: Day of the month. For example, "10" would represent the 10th day of the month.
* mm: Month of the year, ranging from 1 to 12. This represents the months from January to December. For example, "01" would represent January.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import static java.util.Objects.requireNonNull;
import static seedu.foodrem.commons.enums.CommandType.STATS_COMMAND;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
Expand All @@ -16,7 +14,7 @@
import seedu.foodrem.logic.commands.CommandResult;
import seedu.foodrem.model.Model;
import seedu.foodrem.model.item.Item;
import seedu.foodrem.model.item.itemcomparators.ItemCostComparator;
import seedu.foodrem.model.item.itemcomparators.ItemValueComparator;
import seedu.foodrem.model.tag.Tag;
import seedu.foodrem.viewmodels.Stats;

Expand All @@ -40,15 +38,18 @@ public static String getUsage() {
}

private List<Item> getTopThreeExpensiveItems(List<Item> itemList) {
final List<Item> copy = new ArrayList<>(itemList);
copy.sort(new ItemCostComparator().reversed());
return copy.subList(0, 3);
return itemList.stream()
.sorted(new ItemValueComparator().reversed())
.limit(3)
.collect(Collectors.toList());
}

private double getAmountWasted(List<Item> itemList) {
return itemList.stream()
.filter(i -> !i.getQuantity().isZero() && i.getExpiryDate().isAfterExpiryDate(LocalDate.now()))
.map(i -> i.getPrice().getItemPrice()).reduce(0.0, Double::sum);
.filter(Item::hasNonZeroQuantity)
.filter(Item::isExpired)
.map(Item::getItemValue)
.reduce(0.0, Double::sum);
}

private List<Tag> getTopThreeCommonTags(List<Item> itemList, List<Tag> tagList) {
Expand Down
30 changes: 27 additions & 3 deletions src/main/java/seedu/foodrem/model/item/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static java.util.Objects.requireNonNull;
import static seedu.foodrem.commons.util.CollectionUtil.requireAllNonNull;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -50,6 +51,12 @@ public Item(ItemName name,
ItemRemark remarks,
Set<Tag> tagSet) {
requireAllNonNull(name, quantity, unit, boughtDate, expiryDate, price, remarks, tagSet);

// Same Bought date and Expiry date is accepted
if (!boughtDate.isNotSet() && !expiryDate.isNotSet() && boughtDate.isAfterDate(expiryDate)) {
throw new IllegalArgumentException("The item bought date should not be after the item expiry date.");
}

this.name = name;
this.quantity = quantity;
this.unit = unit;
Expand Down Expand Up @@ -106,12 +113,29 @@ public ItemRemark getRemarks() {
return remarks;
}

// TODO: Possibly refactor to avoid using getter methods in ItemPrice and ItemQuantity fields
/**
* Returns true if the item has expired and false otherwise
*
* @return The total value of purchasing the specified units of the item.
*/
public boolean isExpired() {
return expiryDate.isAfterOrOnDate(LocalDate.now());
}

/**
* Returns true if the item quantity is not zero and false otherwise.
*
* @return The total value of purchasing the specified units of the item.
*/
public boolean hasNonZeroQuantity() {
return !quantity.isZero();
}

/**
* @return The total cost of purchasing the specified units of the item.
* @return The total value of purchasing the specified units of the item.
*/
public double getItemCost() {
public double getItemValue() {
// TODO: Possibly refactor to avoid using getter methods in ItemPrice and ItemQuantity fields
return price.getItemPrice() * quantity.getItemQuantity();
}

Expand Down
39 changes: 10 additions & 29 deletions src/main/java/seedu/foodrem/model/item/ItemBoughtDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,40 @@
import static java.util.Objects.requireNonNull;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import seedu.foodrem.model.item.itemvalidators.ItemBoughtDateValidator;

/**
* Represents an item date in an {@link Item}.
* Represents an item bought date in an {@link Item}.
* Guarantees: details are present and not null, field values are validated, immutable.
*/
public class ItemBoughtDate {
// Remember to change relevant messages when changing the regex.
public static final String BOUGHT_DATE_PATTERN_REGEX = "dd-MM-uuuu";
public static final DateTimeFormatter BOUGHT_DATE_FORMATTER = DateTimeFormatter
.ofPattern(BOUGHT_DATE_PATTERN_REGEX);
public class ItemBoughtDate extends ItemDate {

private static final ItemBoughtDate NOT_SET_BOUGHT_DATE = new ItemBoughtDate(LocalDate.MIN);

private final LocalDate boughtDate;

/**
* Constructs an boughtDate.
*
* @param date a localDate that represents the boughtDate of the
* format {@link ItemBoughtDate#BOUGHT_DATE_FORMATTER}
* format {@link ItemDate#DATE_PATTERN_REGEX}
*/
private ItemBoughtDate(LocalDate date) {
boughtDate = date;
super(date);
}

/**
* Produces a boughtDate object.
*
* @param dateString a string that represents the boughtDate of the
* format {@link ItemBoughtDate#BOUGHT_DATE_FORMATTER}
* format {@link ItemDate#DATE_PATTERN_REGEX}
*/
public static ItemBoughtDate of(String dateString) {
requireNonNull(dateString);
if (dateString.isBlank()) {
return NOT_SET_BOUGHT_DATE;
}
ItemBoughtDateValidator.validate(dateString);
return new ItemBoughtDate(LocalDate.parse(dateString, BOUGHT_DATE_FORMATTER));
return new ItemBoughtDate(LocalDate.parse(dateString, ItemDate.DATE_FORMATTER));
}

/**
Expand All @@ -54,42 +47,30 @@ public boolean isNotSet() {
}

/**
* Returns {@code true} if both {@link ItemBoughtDate#boughtDate} have the same date by
* Returns {@code true} if both {@link ItemBoughtDate} have the same date by
* {@link LocalDate#equals(Object)}.
*/
@Override
public boolean equals(Object other) {
return other == this
|| (other != NOT_SET_BOUGHT_DATE
&& other instanceof ItemBoughtDate
&& boughtDate.equals(((ItemBoughtDate) other).boughtDate));
}

/**
* Compares two item bought dates. The method returns 0 if the bought date is equal to the other
* bought date.
* A value less than 0 is returned if the bought date is less than the other bought date (earlier) and
* a value greater than 0 if the bought date is greater than the other bought date (later).
*
* @param other The ItemBoughtDate to compare this ItemBoughtDate against.
*/
public int compareTo(ItemBoughtDate other) {
return boughtDate.compareTo(other.boughtDate);
&& getDate().equals(((ItemBoughtDate) other).getDate()));
}

/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return boughtDate.hashCode();
return super.hashCode();
}

/**
* {@inheritDoc}
*/
@Override
public String toString() {
return this == NOT_SET_BOUGHT_DATE ? "" : boughtDate.format(BOUGHT_DATE_FORMATTER);
return this == NOT_SET_BOUGHT_DATE ? "" : super.toString();
}
}
73 changes: 73 additions & 0 deletions src/main/java/seedu/foodrem/model/item/ItemDate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package seedu.foodrem.model.item;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
* Represents an item date in an {@link Item}.
* Guarantees: details are present and not null, immutable.
*/
public abstract class ItemDate {
// Remember to change relevant messages displayed to users when changing the regex.
public static final String DATE_PATTERN_REGEX = "dd-MM-uuuu";
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter
.ofPattern(DATE_PATTERN_REGEX);

private final LocalDate date;

protected ItemDate(LocalDate date) {
this.date = date;
}

public LocalDate getDate() {
return date;
}

public int compareTo(ItemDate other) {
return date.compareTo(other.date);
}

/**
* Returns true if the given datetime object is after or on the same date.
*
* @param datetime The datetime object to compare against
*/
public boolean isAfterOrOnDate(LocalDate datetime) {
return date.isAfter(datetime.minusDays(1));
}

/**
* Returns true if the given ItemDate object is after the date.
*
* @param itemDate The ItemDate object to compare against
*/
public boolean isAfterDate(ItemDate itemDate) {
return date.isAfter(itemDate.date);
}

/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object other) {
return other == this
&& other instanceof ItemDate
&& date.equals(((ItemDate) other).date);
}

/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return date.hashCode();
}

/**
* {@inheritDoc}
*/
@Override
public String toString() {
return date.format(DATE_FORMATTER);
}
}
Loading

0 comments on commit d4acccd

Please sign in to comment.