From c4a40c1068ae3ef242d79b9688ff52caeb68a89d Mon Sep 17 00:00:00 2001 From: Madeleine Stamp Date: Thu, 23 Apr 2026 12:41:10 -0600 Subject: [PATCH] Update 2 files - /src/main/java/com/example/demo/controller/RecipeUploadController.java - /src/main/java/com/example/demo/controller/RecipeUploadForm.java --- .../controller/RecipeUploadController.java | 73 +++-- .../demo/controller/RecipeUploadForm.java | 256 ++++-------------- 2 files changed, 96 insertions(+), 233 deletions(-) diff --git a/src/main/java/com/example/demo/controller/RecipeUploadController.java b/src/main/java/com/example/demo/controller/RecipeUploadController.java index 7f860d1..1477ea8 100644 --- a/src/main/java/com/example/demo/controller/RecipeUploadController.java +++ b/src/main/java/com/example/demo/controller/RecipeUploadController.java @@ -2,6 +2,7 @@ package com.example.demo.controller; import java.io.IOException; import java.math.BigDecimal; +import java.math.RoundingMode; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -40,21 +41,13 @@ public class RecipeUploadController { @PostMapping(value = "/api/recipes/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity createRecipeWithUpload(@ModelAttribute RecipeUploadForm form, Principal principal) { try { - System.out.println("UPLOAD ENDPOINT HIT"); - System.out.println("Title: " + form.getTitle()); - System.out.println("Image present: " + (form.getImage() != null && !form.getImage().isEmpty())); - RecipeDto dto = buildRecipeDto(form, true); - - System.out.println("Image DTO count: " + (dto.getImages() == null ? 0 : dto.getImages().size())); - String currentUsername = principal.getName(); RecipeDto saved = recipeService.saveRecipe(dto, currentUsername); - - System.out.println("Saved recipe id: " + saved.getId()); - return new ResponseEntity<>(saved, HttpStatus.CREATED); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.BAD_REQUEST) @@ -68,21 +61,13 @@ public class RecipeUploadController { @ModelAttribute RecipeUploadForm form, Principal principal) { try { - System.out.println("UPDATE UPLOAD ENDPOINT HIT"); - System.out.println("Recipe id: " + id); - System.out.println("Title: " + form.getTitle()); - System.out.println("Image present: " + (form.getImage() != null && !form.getImage().isEmpty())); - System.out.println("Remove image: " + form.getRemoveImage()); - RecipeDto dto = buildRecipeDto(form, false); - String currentUsername = principal.getName(); RecipeDto updated = recipeService.updateRecipe(dto, id, currentUsername); - - System.out.println("Updated recipe id: " + updated.getId()); - return new ResponseEntity<>(updated, HttpStatus.OK); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(HttpStatus.BAD_REQUEST) @@ -109,9 +94,9 @@ public class RecipeUploadController { } String quantityString = getListValue(form.getIngredientQuantity(), i); - String quantity = null; + BigDecimal quantity = null; if (quantityString != null && !quantityString.isBlank()) { - quantity = new String(quantityString.trim()); + quantity = parseQuantity(quantityString.trim()); } String unit = getListValue(form.getIngredientUnit(), i); @@ -119,7 +104,7 @@ public class RecipeUploadController { ingredientDtos.add(new RecipeIngredientDto( ingredientName.trim(), - quantity != null ? quantity.trim() : "", + quantity, unit != null ? unit.trim() : "", notes != null ? notes.trim() : "")); } @@ -156,14 +141,11 @@ public class RecipeUploadController { String imageUrl = saveUploadedFile(imageFile); imageDtos.add(new ImageDto(imageUrl)); dto.setImages(imageDtos); - System.out.println("Saved file path: " + imageUrl); } else if (removeImage) { - // Empty list means remove all images on update dto.setImages(new ArrayList<>()); } else if (isCreate) { dto.setImages(new ArrayList<>()); } else { - // Null on update means keep the existing image as-is dto.setImages(null); } @@ -188,6 +170,45 @@ public class RecipeUploadController { return value == null ? null : value.trim(); } + private BigDecimal parseQuantity(String value) { + String cleaned = value.trim(); + + if (cleaned.matches("^\\d+(\\.\\d+)?$")) { + return new BigDecimal(cleaned); + } + + if (cleaned.matches("^\\d+/\\d+$")) { + String[] parts = cleaned.split("/"); + BigDecimal numerator = new BigDecimal(parts[0]); + BigDecimal denominator = new BigDecimal(parts[1]); + + if (denominator.compareTo(BigDecimal.ZERO) == 0) { + throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); + } + + return numerator.divide(denominator, 8, RoundingMode.HALF_UP).stripTrailingZeros(); + } + + if (cleaned.matches("^\\d+\\s+\\d+/\\d+$")) { + String[] mixed = cleaned.split("\\s+"); + BigDecimal whole = new BigDecimal(mixed[0]); + + String[] fraction = mixed[1].split("/"); + BigDecimal numerator = new BigDecimal(fraction[0]); + BigDecimal denominator = new BigDecimal(fraction[1]); + + if (denominator.compareTo(BigDecimal.ZERO) == 0) { + throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); + } + + BigDecimal fractionalPart = numerator.divide(denominator, 8, RoundingMode.HALF_UP); + return whole.add(fractionalPart).stripTrailingZeros(); + } + + throw new IllegalArgumentException( + "Invalid ingredient quantity: " + value + ". Use values like 1, 1.5, 1/4, or 2 1/2."); + } + private String saveUploadedFile(MultipartFile file) throws IOException { String originalFilename = StringUtils.cleanPath(file.getOriginalFilename()); String extension = ""; diff --git a/src/main/java/com/example/demo/controller/RecipeUploadForm.java b/src/main/java/com/example/demo/controller/RecipeUploadForm.java index 1477ea8..9342811 100644 --- a/src/main/java/com/example/demo/controller/RecipeUploadForm.java +++ b/src/main/java/com/example/demo/controller/RecipeUploadForm.java @@ -1,233 +1,75 @@ package com.example.demo.controller; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.security.Principal; -import java.util.ArrayList; import java.util.List; -import java.util.UUID; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.example.demo.dto.ImageDto; -import com.example.demo.dto.RecipeDto; -import com.example.demo.dto.RecipeIngredientDto; -import com.example.demo.dto.StepDto; -import com.example.demo.dto.TagDto; -import com.example.demo.service.RecipeService; +public class RecipeUploadForm { -@RestController -public class RecipeUploadController { + private String title; + private String description; + private String prepTimeMinutes; + private String cookTimeMinutes; + private String servings; + private String cost; - private final RecipeService recipeService; + private List ingredientName; + private List ingredientQuantity; + private List ingredientUnit; + private List ingredientNotes; - public RecipeUploadController(RecipeService recipeService) { - this.recipeService = recipeService; - } + private List stepInstruction; + private List tags; - @PostMapping(value = "/api/recipes/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity createRecipeWithUpload(@ModelAttribute RecipeUploadForm form, Principal principal) { - try { - RecipeDto dto = buildRecipeDto(form, true); - String currentUsername = principal.getName(); - RecipeDto saved = recipeService.saveRecipe(dto, currentUsername); - return new ResponseEntity<>(saved, HttpStatus.CREATED); + private MultipartFile image; + private Boolean removeImage; - } catch (IllegalArgumentException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body("Recipe creation failed: " + e.getMessage()); - } - } + public RecipeUploadForm() {} - @PostMapping(value = "/api/recipes/{id}/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity updateRecipeWithUpload( - @PathVariable Integer id, - @ModelAttribute RecipeUploadForm form, - Principal principal) { - try { - RecipeDto dto = buildRecipeDto(form, false); - String currentUsername = principal.getName(); - RecipeDto updated = recipeService.updateRecipe(dto, id, currentUsername); - return new ResponseEntity<>(updated, HttpStatus.OK); + // ===== Basic fields ===== + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } - } catch (IllegalArgumentException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body("Recipe update failed: " + e.getMessage()); - } - } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } - private RecipeDto buildRecipeDto(RecipeUploadForm form, boolean isCreate) throws IOException { - RecipeDto dto = new RecipeDto(); - dto.setTitle(safeTrim(form.getTitle())); - dto.setDescription(safeTrim(form.getDescription())); - dto.setPrepTimeMinutes(parseInteger(form.getPrepTimeMinutes())); - dto.setCookTimeMinutes(parseInteger(form.getCookTimeMinutes())); - dto.setServings(parseInteger(form.getServings())); - dto.setStatus("DRAFT"); - dto.setCost(parseInteger(form.getCost())); + public String getPrepTimeMinutes() { return prepTimeMinutes; } + public void setPrepTimeMinutes(String prepTimeMinutes) { this.prepTimeMinutes = prepTimeMinutes; } - List ingredientDtos = new ArrayList<>(); - if (form.getIngredientName() != null) { - for (int i = 0; i < form.getIngredientName().size(); i++) { - String ingredientName = getListValue(form.getIngredientName(), i); - if (ingredientName == null || ingredientName.isBlank()) { - continue; - } + public String getCookTimeMinutes() { return cookTimeMinutes; } + public void setCookTimeMinutes(String cookTimeMinutes) { this.cookTimeMinutes = cookTimeMinutes; } - String quantityString = getListValue(form.getIngredientQuantity(), i); - BigDecimal quantity = null; - if (quantityString != null && !quantityString.isBlank()) { - quantity = parseQuantity(quantityString.trim()); - } + public String getServings() { return servings; } + public void setServings(String servings) { this.servings = servings; } - String unit = getListValue(form.getIngredientUnit(), i); - String notes = getListValue(form.getIngredientNotes(), i); + public String getCost() { return cost; } + public void setCost(String cost) { this.cost = cost; } - ingredientDtos.add(new RecipeIngredientDto( - ingredientName.trim(), - quantity, - unit != null ? unit.trim() : "", - notes != null ? notes.trim() : "")); - } - } - dto.setIngredients(ingredientDtos); + // ===== Ingredients ===== + public List getIngredientName() { return ingredientName; } + public void setIngredientName(List ingredientName) { this.ingredientName = ingredientName; } - List stepDtos = new ArrayList<>(); - if (form.getStepInstruction() != null) { - for (int i = 0; i < form.getStepInstruction().size(); i++) { - String instruction = form.getStepInstruction().get(i); - if (instruction == null || instruction.isBlank()) { - continue; - } - stepDtos.add(new StepDto(i + 1, instruction.trim())); - } - } - dto.setSteps(stepDtos); + public List getIngredientQuantity() { return ingredientQuantity; } + public void setIngredientQuantity(List ingredientQuantity) { this.ingredientQuantity = ingredientQuantity; } - List tagDtos = new ArrayList<>(); - if (form.getTags() != null) { - for (String tag : form.getTags()) { - if (tag != null && !tag.isBlank()) { - tagDtos.add(new TagDto(tag.trim())); - } - } - } - dto.setTags(tagDtos); + public List getIngredientUnit() { return ingredientUnit; } + public void setIngredientUnit(List ingredientUnit) { this.ingredientUnit = ingredientUnit; } - MultipartFile imageFile = form.getImage(); - boolean removeImage = Boolean.TRUE.equals(form.getRemoveImage()); + public List getIngredientNotes() { return ingredientNotes; } + public void setIngredientNotes(List ingredientNotes) { this.ingredientNotes = ingredientNotes; } - if (imageFile != null && !imageFile.isEmpty()) { - List imageDtos = new ArrayList<>(); - String imageUrl = saveUploadedFile(imageFile); - imageDtos.add(new ImageDto(imageUrl)); - dto.setImages(imageDtos); - } else if (removeImage) { - dto.setImages(new ArrayList<>()); - } else if (isCreate) { - dto.setImages(new ArrayList<>()); - } else { - dto.setImages(null); - } + // ===== Steps ===== + public List getStepInstruction() { return stepInstruction; } + public void setStepInstruction(List stepInstruction) { this.stepInstruction = stepInstruction; } - return dto; - } + // ===== Tags ===== + public List getTags() { return tags; } + public void setTags(List tags) { this.tags = tags; } - private Integer parseInteger(String value) { - if (value == null || value.isBlank()) { - return null; - } - return Integer.valueOf(value.trim()); - } + // ===== Image ===== + public MultipartFile getImage() { return image; } + public void setImage(MultipartFile image) { this.image = image; } - private String getListValue(List list, int index) { - if (list == null || index >= list.size()) { - return null; - } - return list.get(index); - } - - private String safeTrim(String value) { - return value == null ? null : value.trim(); - } - - private BigDecimal parseQuantity(String value) { - String cleaned = value.trim(); - - if (cleaned.matches("^\\d+(\\.\\d+)?$")) { - return new BigDecimal(cleaned); - } - - if (cleaned.matches("^\\d+/\\d+$")) { - String[] parts = cleaned.split("/"); - BigDecimal numerator = new BigDecimal(parts[0]); - BigDecimal denominator = new BigDecimal(parts[1]); - - if (denominator.compareTo(BigDecimal.ZERO) == 0) { - throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); - } - - return numerator.divide(denominator, 8, RoundingMode.HALF_UP).stripTrailingZeros(); - } - - if (cleaned.matches("^\\d+\\s+\\d+/\\d+$")) { - String[] mixed = cleaned.split("\\s+"); - BigDecimal whole = new BigDecimal(mixed[0]); - - String[] fraction = mixed[1].split("/"); - BigDecimal numerator = new BigDecimal(fraction[0]); - BigDecimal denominator = new BigDecimal(fraction[1]); - - if (denominator.compareTo(BigDecimal.ZERO) == 0) { - throw new IllegalArgumentException("Ingredient quantity cannot have a zero denominator."); - } - - BigDecimal fractionalPart = numerator.divide(denominator, 8, RoundingMode.HALF_UP); - return whole.add(fractionalPart).stripTrailingZeros(); - } - - throw new IllegalArgumentException( - "Invalid ingredient quantity: " + value + ". Use values like 1, 1.5, 1/4, or 2 1/2."); - } - - private String saveUploadedFile(MultipartFile file) throws IOException { - String originalFilename = StringUtils.cleanPath(file.getOriginalFilename()); - String extension = ""; - - int dotIndex = originalFilename.lastIndexOf('.'); - if (dotIndex >= 0) { - extension = originalFilename.substring(dotIndex); - } - - String storedFilename = UUID.randomUUID() + extension; - - Path uploadDir = Paths.get("uploads"); - if (!Files.exists(uploadDir)) { - Files.createDirectories(uploadDir); - } - - Path destination = uploadDir.resolve(storedFilename); - Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING); - - return "/uploads/" + storedFilename; - } + public Boolean getRemoveImage() { return removeImage; } + public void setRemoveImage(Boolean removeImage) { this.removeImage = removeImage; } } \ No newline at end of file