diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/ShoppingListController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/ShoppingListController.java index 103d16a027787af01f48ee2589928cf27e717b53..352447b28b4114bf14b0b099861f19b9a1580c34 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/ShoppingListController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/ShoppingListController.java @@ -134,8 +134,10 @@ public class ShoppingListController { if(product.isEmpty()) return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + System.out.println("Removing product from shopping list : " + shoppingListId + " - " + ean); + Optional<ShoppingList> returnVal = shoppingListService - .removeProductFromShoppingList(Long.parseLong(shoppingListId), Long.parseLong(ean)); + .removeProductFromShoppingList(Long.parseLong(ean), Long.parseLong(shoppingListId)); return returnVal.map(list -> ResponseEntity.status(HttpStatus.OK).body(list)) .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/FridgeController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/FridgeController.java index 115760fa842ff53a8733956e82227e227646291e..bf2861a67a389e5c622f9e6299ea08b60f883650 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/FridgeController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/FridgeController.java @@ -114,24 +114,5 @@ public class FridgeController { return fridgeService.wasteProductFromFridge(fridgeProductId).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } - /** - * Deletes all products in a fridge - * @param fridgeId the id of the fridge - * @return success if the products were deleted, bad request if the fridge doesn't exist - * - @DeleteMapping("/delete/all/{fridgeId}") - public ResponseEntity<String> deleteAllProductsInFridge(@PathVariable("fridgeId") long fridgeId) { - try { - boolean success = fridgeService.deleteAllProductsInFridge(fridgeId); - if (success){ - return ResponseEntity.ok("Success"); - } - return ResponseEntity.badRequest().body("Fridge not found"); - } catch (Exception e) { - return ResponseEntity.status(500).body("Internal server error"); - } - } - - */ } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/GroupController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/GroupController.java index a33ab537ddf929fb423fc6ab82aed0e7af3babf0..650976945c6354325005bce0f5194e17466ea404 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/GroupController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/GroupController.java @@ -19,6 +19,7 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.util.*; +import java.util.stream.Collectors; /** * Controller for groups API, providing endpoints for group management @@ -324,4 +325,59 @@ public class GroupController { } } + /** + * Handles the HTTP DELETE request to remove a user from a group. + * @param auth the authentication object containing the username of the user + * @return a ResponseEntity object containing the list of groups the user is associated with and an HTTP status code of 200, + */ + @GetMapping("/") + public ResponseEntity<List<UserGroupAsso>> getAllGroupsByUser(Authentication auth) { + return ResponseEntity.ok(groupService.getUserGroupAssoByUserName(auth.getName())); + } + + + /** + * Handles the HTTP DELETE request to remove a user from a group. + * @param groupId the ID of the group to get the members of + * @param username the username of the user to remove from the group + * @param auth the authentication object containing the username of the user + * @return a ResponseEntity object containing the list of groups the + * user is associated with and an HTTP status code of 200, + */ + @DeleteMapping("/removeUser/{groupId}/{username}") + public ResponseEntity<?> removeUserFromGroup(@PathVariable("groupId") long groupId, + @PathVariable("username") String username, + Authentication auth) { + Optional<User> groupAdminOpt = userService.getUserFromUsername(auth.getName()); + if (groupAdminOpt.isPresent()) { + User groupAdmin = groupAdminOpt.get(); + if (!(groupService.isUserAssociatedWithGroup(groupAdmin.getUsername(), groupId) + && (groupService.getUserGroupAssoAuthority(groupAdmin.getUsername(), groupId).equals("ADMIN")) + || groupAdmin.getUsername().equals(username))) + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You are not authorized to remove this user."); + } + + Optional<Group> groupOpt = groupService.getGroupById(groupId); + Optional<User> userOpt = userService.getUserFromUsername(username); + + if (groupOpt.isEmpty() || userOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + User user = userOpt.get(); + UserGroupAsso userGroupAsso = user.getGroup().stream() + .filter(asso -> asso.getGroup().getGroupId() == groupId) + .findFirst() + .orElse(null); + + if (userGroupAsso != null) { + groupService.removeUserFromGroup(userGroupAsso); + return ResponseEntity.ok("User removed successfully."); + } else { + return ResponseEntity.notFound().build(); + } + } + + + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/WasteController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/WasteController.java index efb3e0361b544a547bcb51eba8c0bb1632d464e6..ff28172f2623650fcb510fb0e702bcd8df6143bb 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/WasteController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/group/WasteController.java @@ -79,4 +79,40 @@ public class WasteController { return wasteService.getCakeDiagram(groupId).map(ResponseEntity::ok).orElseGet(()->ResponseEntity.notFound().build()); } + + /** + * Get the information of the last months of a specific group. + * + * @param groupId the id of the group to get the information for + * @return a ResponseEntity object containing an array of doubles representing the waste for each category + * in the last four months, or a not found response if the group does not exist or has no waste data + */ + @GetMapping("/statistic/lastMonths/{groupId}") + public ResponseEntity<double[]> getInformationOfLastMoths(@PathVariable("groupId") long groupId){ + return wasteService.getLastMonth(groupId).map(ResponseEntity::ok).orElseGet(()->ResponseEntity.notFound().build()); + } + + /** + * Retrieves the amount of money lost due to expired products in a specific group. + * The amount is calculated based on the total cost of the expired products. + * + * @param groupId the ID of the group to retrieve the lost money from + * @return a ResponseEntity with the lost money as a Double if found, or a ResponseEntity with status 404 if the group is not found + */ + @GetMapping("/statistic/lostMoney/{groupId}") + public ResponseEntity<Double> getLostMoney(@PathVariable("groupId") long groupId){ + return wasteService.getLostMoney(groupId).map(ResponseEntity::ok).orElseGet(()->ResponseEntity.notFound().build()); + } + + /** + * Retrieves the amount of CO2 emitted annually per person in a specific group. + * + * @param groupId the ID of the group to retrieve the statistic for + * @return a ResponseEntity containing the amount of CO2 emitted annually per person in the group, + * or a ResponseEntity with HTTP status 404 (not found) if the group or data is not found + */ + @GetMapping("/statistic/annuallyCO2/{groupId}") + public ResponseEntity<Double> getCO2Annually(@PathVariable("groupId") long groupId){ + return wasteService.getCO2PerPerson(groupId).map(ResponseEntity::ok).orElseGet(()->ResponseEntity.notFound().build()); + } } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java index b7755e34186891752ec36cbc873b948d166e97dc..5d81db83a00577a97135789baf8d1f3b8e6880b9 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java @@ -8,6 +8,7 @@ import ntnu.idatt2016.v233.SmartMat.service.AllergyService; import ntnu.idatt2016.v233.SmartMat.service.product.CategoryService; import ntnu.idatt2016.v233.SmartMat.service.product.ProductService; import ntnu.idatt2016.v233.SmartMat.util.CategoryUtil; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -110,13 +111,13 @@ public class ProductController { * @return The product that was deleted. */ @DeleteMapping("ean/{ean}") - public ResponseEntity<Product> deleteProduct(@PathVariable long ean) { + public ResponseEntity<String> deleteProduct(@PathVariable long ean) { Optional<Product> product = productService.getProductById(ean); if(product.isPresent()) { productService.deleteProductById(product.get().getEan()); - return ResponseEntity.ok(product.get()); + return ResponseEntity.ok("Product deleted"); } - return ResponseEntity.notFound().build(); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product not found"); } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java index 9bb5d58d78aef857d41d92a7d0d3cf8d12afc0b4..15290417f52ed467a8a7ea0aef70fb2197127938 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java @@ -6,16 +6,19 @@ import ntnu.idatt2016.v233.SmartMat.dto.request.AllergyRequest; import ntnu.idatt2016.v233.SmartMat.dto.request.RegisterUserRequest; import ntnu.idatt2016.v233.SmartMat.dto.enums.Authority; import ntnu.idatt2016.v233.SmartMat.dto.request.UpdateUserRequest; +import ntnu.idatt2016.v233.SmartMat.entity.product.Allergy; import ntnu.idatt2016.v233.SmartMat.entity.user.User; import ntnu.idatt2016.v233.SmartMat.service.user.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Optional; +import java.util.stream.Collectors; /** @@ -105,14 +108,13 @@ public class UserController { * @return a ResponseEntity with a boolean indicating whether the operation was successful */ @PostMapping("/addAllergy") - public ResponseEntity<Boolean> addAllergyToUser(@RequestBody AllergyRequest allergyRequest) { - try { - return userService.addAllergyToUser(allergyRequest.getUsername(), allergyRequest.getAllergyName()) - .map(user -> ResponseEntity.ok(user.getAllergies().size() > 0)) - .orElseGet(() -> ResponseEntity.notFound().build()); - }catch (Exception e){ - return ResponseEntity.status(409).body(false); - } + public ResponseEntity<String> addAllergyToUser(@RequestBody AllergyRequest allergyRequest, + Authentication authentication) { + if(!allergyRequest.getUsername().equals(authentication.getName()) && + !authentication.getAuthorities().contains(new SimpleGrantedAuthority(Authority.ADMIN.name()))) + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + + return userService.addAllergyToUser(allergyRequest.getUsername(), allergyRequest.getAllergyName()); } /** @@ -122,14 +124,14 @@ public class UserController { * @return a ResponseEntity with a boolean indicating whether the operation was successful */ @DeleteMapping("/deleteAllergy") - public ResponseEntity<Boolean> deleteAllergyFromUser(@RequestBody AllergyRequest allergyRequest) { - try { - return userService.deleteAllergy(allergyRequest.getUsername(), allergyRequest.getAllergyName()) - .map(user -> ResponseEntity.ok(true)) - .orElseGet(() -> ResponseEntity.notFound().build()); - }catch (Exception e){ - return ResponseEntity.status(409).body(false); - } + public ResponseEntity<String> deleteAllergyFromUser(@RequestBody AllergyRequest allergyRequest, + Authentication authentication) { + if(!allergyRequest.getUsername().equals(authentication.getName()) && + !authentication.getAuthorities().contains(new SimpleGrantedAuthority(Authority.ADMIN.name()))) + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + + return userService.removeAllergyFromUser(allergyRequest.getUsername(), allergyRequest.getAllergyName()); + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/FridgeProductRequest.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/FridgeProductRequest.java index bbe5b8717bb183192619d446f01a6d6ab502974e..0df58b4fe4b21788385ec234ef449307c5b7239c 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/FridgeProductRequest.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/FridgeProductRequest.java @@ -3,7 +3,6 @@ package ntnu.idatt2016.v233.SmartMat.dto.request; /** * FridgeProductRequest is a record class representing a request to add a product to a fridge. * @param groupId the id of the group - * @param productId the id of the product */ -public record FridgeProductRequest(long fridgeProductId, long groupId, long ean, int amount, int days) { +public record FridgeProductRequest(long fridgeProductId, long groupId, long ean, int amount, int days, double price) { } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Recipe.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Recipe.java index c42c4093fbf6347cbe811bb3535e97acef616538..b4ce8bf285511ec93e0bb6fdd45affb96aad094f 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Recipe.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Recipe.java @@ -40,6 +40,12 @@ public class Recipe { @Column(name = "recipe_description") String description; + @Column(name = "image_url") + String imageUrl; + + @Column(name = "guide") + String guide; + @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} ,mappedBy = "recipes" diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Waste.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Waste.java index b876946b6c7dd762dad3566334e1210241e223e9..567f074c7863a26219a5ac9227019bdc9ad94203 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Waste.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/Waste.java @@ -53,4 +53,7 @@ public class Waste { @Column(name = "unit") String unit; + @Column(name = "buy_price") + double buyPrice; + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/fridgeProduct/FridgeProductAsso.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/fridgeProduct/FridgeProductAsso.java index 8e82358929612d141ff28269560da43a835fa5dd..eb660c4948666ee397d16796bc5ee2e57a528640 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/fridgeProduct/FridgeProductAsso.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/fridgeProduct/FridgeProductAsso.java @@ -2,6 +2,8 @@ package ntnu.idatt2016.v233.SmartMat.entity.fridgeProduct; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonIncludeProperties; import jakarta.persistence.*; import lombok.*; import ntnu.idatt2016.v233.SmartMat.entity.group.Fridge; @@ -11,7 +13,7 @@ import java.sql.Date; @AllArgsConstructor @NoArgsConstructor -@Getter @Setter +@Data @Entity(name = "fridge_product") @Builder public class FridgeProductAsso { @@ -23,12 +25,13 @@ public class FridgeProductAsso { @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinColumn(name = "fridge_id") - @JsonIgnoreProperties({"products"}) + @JsonIgnore private Fridge fridgeId; @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinColumn(name = "ean") - @JsonIgnoreProperties({"fridges"}) + @JsonIncludeProperties({"HibernateLazyInitializer", "handler", "ean", "name", "description", "category", + "url", "allergies", "bestBefore", "expirationDate", "unit", "amount"}) private Product ean; @Column(name = "purchase_date") @@ -40,5 +43,8 @@ public class FridgeProductAsso { @Column(name = "amount") private double amount; + @Column(name = "buy_price") + private double buyPrice; + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Fridge.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Fridge.java index 18bbfb732438f7ac86aaaca02fde6cdbec14ae63..537c640223ddc5cfe829c703a37ea2965f20e941 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Fridge.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Fridge.java @@ -42,7 +42,7 @@ public class Fridge{ @OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.LAZY, mappedBy = "fridgeId") @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnoreProperties("fridge") + @JsonIgnoreProperties("fridgeId") List<FridgeProductAsso> products; diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Group.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Group.java index c390052ee47bd59e9df60ff40d80a7db45d52d9e..346ccd7fb82b696d451775fedcb542fa0f34b758 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Group.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/Group.java @@ -56,8 +56,10 @@ public class Group { private ShoppingList shoppingList; - @OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, - fetch = FetchType.LAZY, mappedBy = "group") + @OneToMany(cascade = {CascadeType.ALL}, + fetch = FetchType.LAZY, mappedBy = "group", + orphanRemoval = true + ) @JsonIgnoreProperties("group") private List<UserGroupAsso> user = new ArrayList<>(); diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/UserGroupAsso.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/UserGroupAsso.java index 5c90e08c389f8ddf218412386a0bf3ccd156f082..cf3b0fca6be1baeaa5cb6570cd0210d947799a1d 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/UserGroupAsso.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/group/UserGroupAsso.java @@ -1,5 +1,6 @@ package ntnu.idatt2016.v233.SmartMat.entity.group; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; import lombok.*; @@ -26,13 +27,13 @@ public class UserGroupAsso { @ManyToOne @MapsId("username") @JoinColumn(name = "username") - @JsonIgnoreProperties("group") + @JsonIgnore private User user; @ManyToOne @MapsId("group_id") @JoinColumn(name = "group_id") - @JsonIgnoreProperties({"group", "user"}) + @JsonIgnoreProperties({"user", "fridge", "shoppingList"}) private Group group; @Column(name = "primary_group") diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Allergy.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Allergy.java index 77870ee42c8df6c2a87d571952e8d8935b5901c6..dc776c200390eef2a571df2e83054c97b47d08e2 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Allergy.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Allergy.java @@ -9,6 +9,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import ntnu.idatt2016.v233.SmartMat.entity.user.User; +import java.util.ArrayList; import java.util.List; /** @@ -49,4 +50,15 @@ public class Allergy{ @JsonIgnore private List<User> users; + /** + * adds a user to the allergy + * @param tempuser adds a user to the list of users with this allergy + */ + public void addUser(User tempuser) { + if (users == null) + users = new ArrayList<>(); + + users.add(tempuser); + + } } \ No newline at end of file diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java index 3f3840db584c0c651f19a3e2f5d41bdf6b95f3b2..71f295cd4627c3a5f7882d78920f050e395fe266 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java @@ -2,6 +2,7 @@ package ntnu.idatt2016.v233.SmartMat.entity.product; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIncludeProperties; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -52,7 +53,7 @@ public class Product{ @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.LAZY) @JoinColumn(name = "category_name") - @JsonIgnore + @JsonIncludeProperties("categoryName") Category category; @Column(name = "image_url") @@ -72,13 +73,12 @@ public class Product{ @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.LAZY, mappedBy = "products") - @JsonIgnoreProperties({"products", "users"}) + @JsonIncludeProperties("name") List<Allergy> allergies; @OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.LAZY, mappedBy = "ean") @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnoreProperties({"products"}) @JsonIgnore List<FridgeProductAsso> fridges; diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java index 8f9f5967ffd42d4048bf28eb6c6147821fdbc6bd..e857f458d53a0eec4bc8acbec59faa7aee44e97c 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java @@ -55,13 +55,13 @@ public class User implements UserDetails { private Date dateOfBirth; - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @JoinColumn(name = "username") + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user") @JsonIgnore private List<UserGroupAsso> group; - @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, + @ManyToMany(fetch = FetchType.LAZY, + cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, mappedBy = "users") @JsonIgnoreProperties({"users", "products"}) private List<Allergy> allergies; diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/RecipeRepository.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/RecipeRepository.java index 54d98c6c3398fa0ae906bb77e43177a1f3077f76..122fda1f81b8bcef94b3207023c7eb84d4a6edb4 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/RecipeRepository.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/RecipeRepository.java @@ -78,7 +78,7 @@ public interface RecipeRepository extends JpaRepository<Recipe, Long> { List<Object[]> findTop5RecipesWithProductsRaw(@Param("fridgeId") long fridgeId); @Query( value = """ - SELECT r.recipe_id, r.recipe_name,r.recipe_description, COUNT(fp.ean) as product_count + SELECT r.recipe_id, r.recipe_name,r.recipe_description,r.image_url,r.guide, COUNT(fp.ean) as product_count FROM recipe r LEFT JOIN recipe_product rp ON r.recipe_id = rp.recipe_id LEFT JOIN fridge_product fp ON rp.ean = fp.ean AND fp.fridge_id = :fridgeId diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepository.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepository.java index 6363b44990e2779e83fa589673c684939701231d..e1f3278481f0b498efb7b1b4ba4beb203f3a39ec 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepository.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepository.java @@ -47,4 +47,7 @@ public interface GroupRepository extends JpaRepository<Group, Long> { Optional<Group> findByLinkCode(String linkCode); + @Query(value = "SELECT count(*) FROM user_group where group_id = :groupId", nativeQuery = true) + int countAllUserInGroup(@Param("groupId") long groupId); + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/UserGroupAssoRepository.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/UserGroupAssoRepository.java index 77f294fc3cb98207701c5c2bf9eb9c155f2c43eb..07d3739543d9d39527e01eac24b404b0e3621d2e 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/UserGroupAssoRepository.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/repository/group/UserGroupAssoRepository.java @@ -73,4 +73,12 @@ public interface UserGroupAssoRepository extends JpaRepository<UserGroupAsso, Us * @return */ String findAuthorityByUser_UsernameAndGroup_GroupId(String username, long groupId); + + + /** + * Finds all groups a user is a member of + * @param username the username of the user + * @return a list of all groups the user is a member of + */ + List<UserGroupAsso> findAllByUserUsername(String username); } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/ShoppingListService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/ShoppingListService.java index 5caae7454907a07ccc0812547184d93e8e6adce0..c7867e9c482e064e58117c78f316d604d4aed7f6 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/ShoppingListService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/ShoppingListService.java @@ -78,6 +78,7 @@ public class ShoppingListService { shoppingListRepository.findById(shoppingListId).ifPresent(shoppingList -> { productRepository.findById(ean).ifPresent(product -> { shoppingList.addProduct(product); + product.addShoppingList(shoppingList); shoppingListRepository.save(shoppingList); }); }); @@ -92,13 +93,14 @@ public class ShoppingListService { * @return the shopping list that the product was removed from */ public Optional<ShoppingList> removeProductFromShoppingList(long ean, long shoppingListId){ + System.out.println("shopping list status : " + shoppingListRepository.findById(shoppingListId).isPresent()); + shoppingListRepository.findById(shoppingListId).ifPresent(shoppingList -> { - productRepository.findById(ean).ifPresent(product -> { - shoppingList.getProducts().remove(product); - product.getShoppingLists().remove(shoppingList); - productRepository.save(product); - shoppingListRepository.save(shoppingList); - }); + productRepository.findById(ean).ifPresent(product -> { + shoppingList.getProducts().remove(product); + product.getShoppingLists().remove(shoppingList); + shoppingListRepository.save(shoppingList); + }); }); return shoppingListRepository.findById(shoppingListId); diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/FridgeService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/FridgeService.java index 0c0a92dbb3fcdb31c1312e562f184a48ae02ac93..9071dfa5f38b503f8519af0a5b80a20aa4eaddc0 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/FridgeService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/FridgeService.java @@ -69,37 +69,46 @@ public class FridgeService { * @param fridgeProductRequest the fridge product request * @return the product that was added to the fridge */ - public Optional<Product> addProductToFridge(FridgeProductRequest fridgeProductRequest) { + public Optional<Object> addProductToFridge(FridgeProductRequest fridgeProductRequest) { Optional<Product> product = productService.getProductById(fridgeProductRequest.ean()); Optional<Fridge> fridge = fridgeRepository.findByGroupGroupId(fridgeProductRequest.groupId()); + double price = 100.0; + if(fridgeProductRequest.price() != 0.0){ + price = fridgeProductRequest.price(); + } if(product.isEmpty() || fridge.isEmpty()) return Optional.empty(); fridge.get().addProduct(FridgeProductAsso.builder() - .fridgeId(fridge.get()) - .ean(product.get()) - .amount(fridgeProductRequest.amount()) - .daysToExpiration(fridgeProductRequest.days()) - .purchaseDate(java.sql.Date.valueOf(LocalDate.now())) + .fridgeId(fridge.get()) + .ean(product.get()) + .amount(fridgeProductRequest.amount()) + .daysToExpiration(fridgeProductRequest.days()) + .purchaseDate(java.sql.Date.valueOf(LocalDate.now())) + .buyPrice(price) .build()); fridgeRepository.save(fridge.get()); - return product; - } + return Optional.of(product); - public Optional<FridgeProductAsso> updateProductInFridge(FridgeProductRequest request) { + } + + public Optional<Object> updateProductInFridge(FridgeProductRequest request) { Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findById(request.fridgeProductId()); if (fridgeProductAsso.isEmpty()) return Optional.empty(); + + Integer amount = request.amount(); + Integer days = request.days(); - int amount = request.amount(); - int days = request.days(); - - fridgeProductAsso.get().setAmount(amount); - fridgeProductAsso.get().setDaysToExpiration(days); + if (amount != null) fridgeProductAsso.get() + .setAmount(request.amount()); + if (days != null) fridgeProductAsso.get() + .setDaysToExpiration(request.days()); + fridgeProductAssoRepo.save(fridgeProductAsso.get()); - return fridgeProductAsso; + return Optional.of(fridgeProductAsso); } @@ -155,12 +164,12 @@ public class FridgeService { * @param amount the amount to delete * @return an optional containing the fridge product if it exists */ - public Optional<FridgeProductAsso> deleteAmountFromFridge(long fridgeProductId, double amount) { + public Optional<Object> deleteAmountFromFridge(long fridgeProductId, double amount) { Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findAllById(fridgeProductId); if(fridgeProductAsso.isEmpty()) return Optional.empty(); FridgeProductAsso fridgeProductAsso1 = fridgeProductAsso.get(); if(amount < fridgeProductAsso1.getAmount() ){ - fridgeProductAsso1.setAmount(fridgeProductAsso1.getAmount() - amount); + fridgeProductAsso1.setAmount(fridgeProductAsso1.getAmount() -amount); return Optional.of(fridgeProductAssoRepo.save(fridgeProductAsso1)); } else { Group group = fridgeProductAsso1.getFridgeId().getGroup(); @@ -168,7 +177,7 @@ public class FridgeService { group.setLevel(GroupUtil.getLevel(group.getPoints())); groupRepository.save(group); fridgeProductAssoRepo.delete(fridgeProductAsso.get()); - return Optional.empty(); + return Optional.of(true); } } @@ -177,7 +186,7 @@ public class FridgeService { * @param fridgeProductId the ID of the fridge product association to delete * @return an Optional containing the saved waste object, or an empty Optional if the fridge product association with the given ID is not found */ - public Optional<Waste> wasteProductFromFridge(long fridgeProductId){ + public Optional<Object> wasteProductFromFridge(long fridgeProductId){ Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findById(fridgeProductId); if(fridgeProductAsso.isEmpty()) return Optional.empty(); FridgeProductAsso fridgeProductAsso1 = fridgeProductAsso.get(); @@ -188,13 +197,7 @@ public class FridgeService { group.setLevel(GroupUtil.getLevel(group.getPoints())); } groupRepository.save(group); - return Optional.of(wasteRepository.save(Waste.builder() - .amount(fridgeProductAsso1.getAmount()) - .unit(fridgeProductAsso1.getEan().getUnit()) - .ean(fridgeProductAsso1.getEan()) - .groupId(fridgeProductAsso1.getFridgeId().getGroup()) - .timestamp(new Timestamp(System.currentTimeMillis())) - .build())); + return Optional.of(wasteRepository.save(Waste.builder().buyPrice(fridgeProductAsso1.getBuyPrice()).amount(fridgeProductAsso1.getAmount()).unit(fridgeProductAsso1.getEan().getUnit()).ean(fridgeProductAsso1.getEan()).groupId(fridgeProductAsso1.getFridgeId().getGroup()).timestamp(new Timestamp(System.currentTimeMillis())).build())); } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/GroupService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/GroupService.java index 484f3ff93f71a911ef9b0bf1424443c5e7389ccc..53daf543777b798f1332672a3ee3ec5831e5fd23 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/GroupService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/GroupService.java @@ -1,5 +1,6 @@ package ntnu.idatt2016.v233.SmartMat.service.group; +import jakarta.transaction.Transactional; import lombok.AllArgsConstructor; import ntnu.idatt2016.v233.SmartMat.entity.ShoppingList; import ntnu.idatt2016.v233.SmartMat.entity.group.Fridge; @@ -219,4 +220,37 @@ public class GroupService { Optional<UserGroupAsso> userGroupAsso = getUserGroupAsso(username, groupId); return userGroupAsso.map(UserGroupAsso::getGroupAuthority).orElseThrow(() -> new IllegalArgumentException("User is not associated with group")); } + + + /** + * Gets all user group associations for a user + * @param username the username of the user + * @return a list of all user group associations for the user + */ + public List<UserGroupAsso> getUserGroupAssoByUserName(String username) { + return userGroupAssoRepository.findAllByUserUsername(username); + } + + /** + * removes user_group relatioon + * @param username the username of the user + * @param groupId the id of the group + * @return true if the user is the owner of the group, false otherwise + */ + @Transactional + public boolean removeUserFromGroup(UserGroupAsso userGroup) { + Group group = groupRepository.findByGroupId(userGroup.getGroup().getGroupId()) + .orElseThrow(() -> new IllegalArgumentException("Group does not exist")); + + group.getUser().remove(userGroup); + + if (group.getUser().isEmpty()) { + groupRepository.delete(group); + } else { + groupRepository.save(group); + } + + userGroupAssoRepository.delete(userGroup); + return true; + } } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/WasteService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/WasteService.java index 8f43267c5e6805f2d2924544274c110db0510b25..dffcfd8e60fe99dc0ee5dbd1747405992dd77318 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/WasteService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/group/WasteService.java @@ -1,6 +1,9 @@ package ntnu.idatt2016.v233.SmartMat.service.group; +import java.nio.channels.FileChannel; +import java.sql.Date; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import ntnu.idatt2016.v233.SmartMat.dto.request.WasteRequest; @@ -77,4 +80,41 @@ public class WasteService { return group.map(value -> StatisticUtil.getNumberOfWasteByCategoryName(wasteRepository.findByGroupId(value).get())); } + /** + * Retrieve an optional array of doubles representing the amount of waste produced in each of the last 4 months for a given group. + * + * @param groupId a long representing the id of the group whose waste production statistics are to be retrieved + * @return an optional array of doubles representing the amount of waste produced in each of the last 4 months for the given group, + * or an empty optional if the group could not be found or no waste was produced in the last 4 months + */ + public Optional<double[]> getLastMonth(long groupId) { + Optional<Group> group = groupRepository.findByGroupId(groupId); + return group.map(value -> StatisticUtil.getNumberOfWasteByLastMonth(wasteRepository.findByGroupId(value).get())); + } + + /** + * Retrieves the lost money in the last month for the group with the given ID. + * + * @param groupId the ID of the group to retrieve the lost money for + * @return an {@code Optional} containing the lost money if the group exists, or empty if it doesn't exist or there are no wastes in the last month + */ + public Optional<Double> getLostMoney(long groupId) { + Optional<Group> group = groupRepository.findByGroupId(groupId); + return group.map(value -> StatisticUtil.getLostMoneyInLastMonth(wasteRepository.findByGroupId(value).get())); + } + + /** + * Calculates the annual average CO2 emissions per person in the specified group. + * + * @param groupId the ID of the group for which to calculate CO2 emissions + * @return an Optional containing the annual average CO2 emissions per person, or empty if the group has no users or does not exist + */ + public Optional<Double> getCO2PerPerson(long groupId){ + Optional<Group> group = groupRepository.findByGroupId(groupId); + int number = groupRepository.countAllUserInGroup(groupId); + if(number == 0 || group.isEmpty()) return Optional.empty(); + List<Waste> wastes = wasteRepository.findByGroupId(group.get()).get(); + return Optional.of(StatisticUtil.getAnnualAverage(wastes,number)); + } + } diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java index 5e900fcae7463b980ff957ba5552a7f6f2a3c5e1..3c21d1971e071e2498c5432d7c982662ebd39799 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java @@ -8,6 +8,8 @@ import ntnu.idatt2016.v233.SmartMat.entity.user.User; import ntnu.idatt2016.v233.SmartMat.repository.AllergyRepository; import ntnu.idatt2016.v233.SmartMat.repository.user.UserRepository; import ntnu.idatt2016.v233.SmartMat.service.RecipeService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -132,23 +134,27 @@ public class UserService { * Adds allergy to user * @param username username of user * @param allergyName name of allergy - * @return user with added allergy - * @throws EntityNotFoundException if user or allergy does not exist + * @return string of allergies */ - public Optional<User> addAllergyToUser(String username, String allergyName){ + public ResponseEntity<String> addAllergyToUser(String username, String allergyName){ Optional<User> user = userRepository.findByUsername(username); Optional<Allergy> allergy = allergyRepository.findByName(allergyName); if (user.isPresent() && allergy.isPresent()){ - user.get().addAllergy(allergy.get()); - return Optional.of(userRepository.save(user.get())); - } else if (!user.isPresent()) { - throw new EntityNotFoundException("User not found"); - } else if (!allergy.isPresent()) { - throw new EntityNotFoundException("Allergy not found"); + if(user.get().getAllergies().contains(allergy.get()) + || allergy.get().getUsers().contains(user.get())) + return ResponseEntity.status(HttpStatus.NOT_MODIFIED) + .body("User already has this allergy"); + + User tempuser = user.get(); + allergy.get().addUser(tempuser); + tempuser.addAllergy(allergy.get()); + return ResponseEntity.ok(userRepository.save(tempuser).getAllergies().stream() + .map(Allergy::getName) + .reduce("", (a, b) -> a + " " + b)); } - return Optional.empty(); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Did not find allergy, our user"); } /** @@ -157,40 +163,23 @@ public class UserService { * @param allergyName name of allergy * @return user with removed allergy */ - public Optional<User> removeAllergyFromUser(String username, String allergyName){ + public ResponseEntity<String> removeAllergyFromUser(String username, String allergyName){ Optional<User> user = userRepository.findByUsername(username); Optional<Allergy> allergy = allergyRepository.findByName(allergyName); if (user.isPresent() && allergy.isPresent()){ + if (!user.get().getAllergies().contains(allergy.get()) + || !allergy.get().getUsers().contains(user.get())) + return ResponseEntity.status(HttpStatus.NOT_MODIFIED) + .body("User does not have this allergy"); + user.get().getAllergies().remove(allergy.get()); - return Optional.of(userRepository.save(user.get())); - } else if (user.isEmpty()) { - throw new EntityNotFoundException("User not found"); - } else if (allergy.isEmpty()) { - throw new EntityNotFoundException("Allergy not found"); + allergy.get().getUsers().remove(user.get()); + return ResponseEntity.ok(userRepository.save(user.get()).getAllergies().stream() + .map(Allergy::getName) + .reduce("", (a, b) -> a + " " + b)); } - return Optional.empty(); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Did not find allergy, our user"); } - /** - * Deletes the specified allergy from the user with the given username. - * - * @param username the username of the user to delete the allergy from - * @param allergyName the name of the allergy to delete - * @return an Optional containing the updated User object if the operation was successful, or an empty Optional otherwise - * @throws EntityNotFoundException if the specified user or allergy cannot be found - */ - public Optional<User> deleteAllergy(String username, String allergyName) { - Optional<User> user = userRepository.findByUsername(username); - Optional<Allergy> allergy = allergyRepository.findByName(allergyName); - - if (user.isPresent() && allergy.isPresent()) { - if(user.get().deleteAllergy(allergy.get())) return Optional.of(userRepository.save(user.get())); - } else if (user.isEmpty()) { - throw new EntityNotFoundException("User not found"); - } else { - throw new EntityNotFoundException("Allergy not found"); - } - return Optional.empty(); - } } \ No newline at end of file diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java index c77016523dfd1161343e576eb1ff5493963f951e..40ba1f50447d72116d2e8def8b1051b41006fcf4 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java @@ -4,7 +4,9 @@ import ntnu.idatt2016.v233.SmartMat.entity.product.Product; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -16,52 +18,33 @@ import java.util.regex.Pattern; public class ProductUtil { - private static final String[] VOLUME_UNITS = {"ml", "cl", "dl", "l", "g", "kg"}; + private static final String VOLUME_REGEX = "(\\d+(\\.\\d+)?)\\s*(mls?|centiliters?|deciliters?|liters?|grams?|kilograms?)"; + private static final Map<String, List<String>> VOLUME_UNIT_VARIATIONS = Map.of( + "ml", List.of("ml", "milliliters", "millilitres"), + "cl", List.of("cl", "centiliters", "centilitres"), + "dl", List.of("dl", "deciliters", "decilitres"), + "l", List.of("l", "liters", "litres"), + "g", List.of("g", "grams"), + "kg", List.of("kg", "kilograms") + ); - /** - * Gets the volume of a product, if it exists - * By looking at the name and description of the product - * it will try to first find the volume in the name, and then in the description - * It uses pre defined volume units to find the volume - * Todo: Make it be able to find volume if the size is definde by count - * @param product The product to get the volume from - * @return The volume of the product, if it exists - */ public static Optional<List<String>> getVolumeFromProduct(Product product) { - for (String desc : Arrays.asList(product.getName(), product.getDescription())) { - List<String> words = List.of(desc.split(" ")); - if (words.size() > 1) { - String volume = ""; - for (String unit : VOLUME_UNITS) { - int i = words.indexOf(unit); - if (i != -1) { - return Optional.of(List.of(words.get(i - 1), unit)); - } - } - - volume = words.stream().map(word -> Arrays.stream(VOLUME_UNITS).map(unit -> { - int index = word.indexOf(unit); - if (index == -1) { - if (!Pattern.matches("[a-zA-Z]+", word) && ProductUtil.hasNumbers(word)) { - return word; - } - return ""; - } - return word.substring(0, index) + " " + word.substring(index); - }).findAny().orElse("")) - .filter(ProductUtil::hasNumbers) - .findAny() - .orElse(""); - if (!volume.equals("")){ - return Optional.of(List.of(volume.split(" "))); + String desc = product.getName() + " " + product.getDescription(); + Matcher matcher = Pattern.compile(VOLUME_REGEX).matcher(desc); + if (matcher.find()) { + String volumeString = matcher.group(1); + double volume = Double.parseDouble(volumeString); + String unitString = matcher.group(3); + for (Map.Entry<String, List<String>> entry : VOLUME_UNIT_VARIATIONS.entrySet()) { + if (entry.getValue().contains(unitString)) { + return Optional.of(List.of(String.valueOf(volume), entry.getKey())); } } } - - return Optional.empty(); } + /** * Checks if a string contains any numbers * @param s The string to check diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtil.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtil.java index 4ec0de83f551862e720a27aa4185cf3d83a92d6d..9c6f63d7dd01cf2dbbec19d354468afd43fdb4e6 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtil.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtil.java @@ -3,14 +3,102 @@ package ntnu.idatt2016.v233.SmartMat.util; import ntnu.idatt2016.v233.SmartMat.entity.Waste; import java.sql.Date; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** * A utility class for calculating statistics related to waste and CO2 emissions. + * @author Pedro Cardona + * @version 1.0 + * @since 03.05.2023 */ public class StatisticUtil { + private final static double CO2_KG_MEAT = (99.48 + 39.72 + 33.3 + 26.87 + 12.31 + 9.87 )/6.0; + private final static double CO2_KG_DAIRY = (23.88 + 4.67 + 3.15)/3; + private final static double CO2_KG_BAKED = (4.45 + 1.7 + 1.57)/3; + private final static double CO2_KG_VEGETABLE = (2.09 + 0.98 + 0.86 + 0.46)/4; + private final static double CO2_KG_OTHER = (CO2_KG_MEAT + CO2_KG_DAIRY + CO2_KG_BAKED + CO2_KG_VEGETABLE)/4.0; + + + /** + * Calculates the annual average amount of CO2 emissions per person based on a list of waste objects and a number of persons. + * @param wastes a list of waste objects + * @param number the number of persons + * @return the annual average amount of CO2 emissions per person + */ + public static double getAnnualAverage(List<Waste> wastes, int number){ + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + List<Double> CO2 = new ArrayList<>(); + for(Waste waste: wastes){ + if(waste.getTimestamp().before(timestamp)) timestamp = waste.getTimestamp(); + CO2.add(getCO2ByCategory(waste.getEan().getCategory().getCategoryName()) * getCorrectRelation(waste.getEan().getUnit()) * waste.getAmount()); + } + return getAnnualAveragePerPerson(CO2,number,timestamp); + } + + + /** + * Returns the amount of CO2 emissions per kg for a given category of waste. + * @param category the waste category + * @return the amount of CO2 emissions per kg + */ + private static double getCO2ByCategory(String category){ + switch (category){ + case "meat, fish and chicken" -> { + return CO2_KG_MEAT; + } + case "baked goods and grains" -> { + return CO2_KG_BAKED; + } + case "dairy and egg" ->{ + return CO2_KG_DAIRY; + } + case "fruit and vegetables" ->{ + return CO2_KG_VEGETABLE; + } + default -> { + return CO2_KG_OTHER; + } + } + } + + /** + * Returns the correct CO2 relation for a given unit of waste. + * @param unit the unit of waste + * @return the correct CO2 relation + */ + private static double getCorrectRelation(String unit){ + switch (unit){ + case "l" -> { + return 0.998; + } + case "kg" -> { + return 1.0; + } + case "g" -> { + return 0.001; + } + case "ml" -> { + return 0.000998; + } + case "cl" -> { + return 0.00998; + } + case "dl" -> { + return 0.0998; + } + default -> { + return 0.1; + } + } + } + /** * Get the annual average amount of CO2 emissions per person. * @@ -19,13 +107,13 @@ public class StatisticUtil { * @param firstDate The date on which the group started tracking their CO2 emissions. * @return The annual average amount of CO2 emissions per person. */ - private static double getAnnualAveragePerPerson(List<Double> co2List, int numberOfPerson, Date firstDate) { + private static double getAnnualAveragePerPerson(List<Double> co2List, int numberOfPerson, Timestamp firstDate) { java.util.Date currentDate = new java.util.Date(); long diffInMillis = currentDate.getTime() - firstDate.getTime(); long diffInDays = diffInMillis / (24 * 60 * 60 * 1000); double co2Sum = 0.0; for (double c02 : co2List) co2Sum += c02; - return co2Sum / (((double) diffInDays / 365.0) * (double) numberOfPerson); + return co2Sum / (((double) (diffInDays +1) / 365.0) * (double) numberOfPerson); } /** @@ -47,4 +135,96 @@ public class StatisticUtil { } return numberOfWastes; } + + /** + * Calculates the sum of waste for each of the four previous months. + * The amount of waste is converted to kilograms if the unit is in liters, grams, milliliters, centiliters, or deciliters. + * + * @param wastes the list of wastes to calculate the sum from + * @return an array of four doubles representing the sum of waste for each of the last four months, + * in the same order as the months are counted backwards from the current month + */ + public static double[] getNumberOfWasteByLastMonth(List<Waste> wastes){ + double[] result = new double[4]; + HashMap<Integer,List<Waste>> hashMap = new HashMap<>(); + + LocalDate localDate = LocalDate.now(); + int currentMonth = localDate.getMonthValue(); + int currentYear = localDate.getYear(); + for(Waste waste : wastes){ + LocalDate localDate1 = waste.getTimestamp().toLocalDateTime().toLocalDate(); + if(currentMonth == localDate1.getMonthValue() && currentYear == localDate1.getYear()){ + hashMap.computeIfAbsent(0, k -> new ArrayList<>()); + hashMap.get(0).add(waste); + } + if(Math.abs((currentMonth-1) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){ + hashMap.computeIfAbsent(1, k -> new ArrayList<>()); + hashMap.get(1).add(waste); + } + if(Math.abs((currentMonth-2) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){ + hashMap.computeIfAbsent(2, k -> new ArrayList<>()); + hashMap.get(2).add(waste); + } + if(Math.abs((currentMonth-3) % 12) == localDate1.getMonthValue() && currentYear == localDate1.getYear()){ + hashMap.computeIfAbsent(3, k -> new ArrayList<>()); + hashMap.get(3).add(waste); + } + } + for(int i = 0; i < 4; i++){ + result[i] = getSumOfWaste(hashMap.get(i)); + } + return result; + } + + /** + * Calculates the sum of waste for a list of wastes, and converts the amounts to kilograms if the unit is in liters, + * grams, milliliters, centiliters, or deciliters. + * + * @param wastes the list of wastes to calculate the sum from + * @return the sum of waste in kilograms + */ + private static double getSumOfWaste(List<Waste> wastes){ + double sum = 0.0; + if(wastes == null){ + wastes = new ArrayList<>(); + } + for(Waste waste: wastes){ + sum += getCorrectRelation(waste.getEan().getUnit()) * waste.getAmount(); + } + return sum; + } + + /** + * Calculates the total amount of money lost due to expired or discarded items in the last 30 days, + * based on the list of wastes passed as a parameter. + * + * @param wastes the list of wastes to be analyzed + * @return the total amount of money lost due to expired or discarded items in the last 30 days + */ + public static double getLostMoneyInLastMonth(List<Waste> wastes){ + List<Waste> wasteList = new ArrayList<>(); + LocalDateTime time = LocalDateTime.now().minusDays(30); + for(Waste waste: wastes){ + if(waste.getTimestamp().after(Timestamp.valueOf(time))){ + wasteList.add(waste); + } + } + return lostMoney(wasteList); + } + + /** + * Calculates the total amount of money lost due to expired or discarded items, + * based on the list of wastes passed as a parameter. + * + * @param wastes the list of wastes to be analyzed + * @return the total amount of money lost due to expired or discarded items + */ + private static double lostMoney(List<Waste> wastes){ + double sum = 0.0; + for(Waste waste: wastes){ + sum += waste.getAmount()*waste.getBuyPrice(); + } + return sum; + } + } \ No newline at end of file diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepositoryTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepositoryTest.java index 69cd0518cdf81c5e27d83ebd15410faa43b321d7..52d6a2934e28d1f12f1083f67fc1dbdcc7218f1d 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepositoryTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/repository/group/GroupRepositoryTest.java @@ -106,24 +106,22 @@ public class GroupRepositoryTest { @Test void groupShouldHaveAchivments(){ - Optional<Group> tempGroupe = groupRepository.findByGroupName("test"); - - - assertTrue(tempGroupe.isPresent()); + Group tempgroup = Group.builder().groupName("testiossxjak").build(); Achievement tempAchievement = Achievement.builder().achievementName("test2").build(); - Group tempGroup = tempGroupe.get(); + tempgroup.setAchievements(new ArrayList<>(Collections.singletonList(tempAchievement))); + groupRepository.save(tempgroup); - tempGroup.setAchievements(new ArrayList<>(Collections.singletonList(tempAchievement))); + Optional<Group> tempGroupe = groupRepository.findByGroupName("testiossxjak"); - groupRepository.save(tempGroup); - assertTrue(groupRepository.findByGroupName("test").isPresent()); + assertTrue(tempGroupe.isPresent()); + - assertTrue(groupRepository.findByGroupName("test").get().getAchievements().contains(tempAchievement)); + assertTrue(tempGroupe.get().getAchievements().contains(tempAchievement)); - assertNull(groupRepository.findByGroupName("test").get().getAchievements().get(0).getGroups()); + assertNull(tempGroupe.get().getAchievements().get(0).getGroups()); } diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java index 3e63f644f1131ff2d1d222386beaa6398b00a65c..42595c72ca2cd9065873429645914e5d5c1b9810 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java @@ -134,7 +134,7 @@ public class ProductServiceTest { // Verify that the service returns the correct volume Optional<List<String>> returnedVolume = productService.getProductVolume(productId); assertTrue(returnedVolume.isPresent()); - assertEquals(List.of("500", "ml"), returnedVolume.get()); + assertEquals(List.of("500.0", "ml"), returnedVolume.get()); } @Test diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java index 0164ae29d869b4a85b47dab4cdd82d95c78d8b1e..2ee4467a78d2727454b1cd6a497527973dca9bac 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java @@ -22,7 +22,7 @@ class ProductUtilTest { @Test void getVolumeFromProduct() { - assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("500.0", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -30,7 +30,7 @@ class ProductUtilTest { .description("Pepsi Original 24x500 ml") .build(); - assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("500.0", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -39,7 +39,7 @@ class ProductUtilTest { .build(); - assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("500.0", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -48,6 +48,6 @@ class ProductUtilTest { .build(); - assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("500.0", "ml"), ProductUtil.getVolumeFromProduct(product).get()); } } \ No newline at end of file diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtils.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtils.java index 3560688c40d2613caf1228f2cfaf11227920866f..fd7f879b95f0162ffc037c36b0b17fb60b5d9f7c 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtils.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/StatisticUtils.java @@ -1,11 +1,6 @@ package ntnu.idatt2016.v233.SmartMat.util; public class StatisticUtils { - private final static double CO2_KG_MEAT = (99.48 + 39.72 + 33.3 + 26.87 + 12.31 + 9.87 )/6.0; - private final static double CO2_KG_DAIRY = (23.88 + 4.67 + 3.15)/3; - private final static double CO2_KG_BAKED = (4.45 + 1.7 + 1.57)/3; - private final static double CO2_KG_VEGETABLE = (2.09 + 0.98 + 0.86 + 0.46)/4; - private final static double CO2_KG_OTHER = (CO2_KG_MEAT + CO2_KG_DAIRY + CO2_KG_BAKED + CO2_KG_VEGETABLE)/4.0; }