Skip to content
This repository was archived by the owner on Jul 24, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/main/java/shef/data/Ingredient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shef.data;

import com.google.appengine.api.datastore.EmbeddedEntity;

public class Ingredient {

private double amount;
private String unit;
private String name;

public Ingredient(double amount, String unit, String name) {
this.amount = amount;
this.unit = unit;
this.name = name;
}

public Ingredient(EmbeddedEntity entity) {
this.amount = (double) entity.getProperty("amount");
this.unit = (String) entity.getProperty("unit");
this.name = (String) entity.getProperty("name");
}

public double amount() {
return amount;
}

public String unit() {
return unit;
}

public String name() {
return name;
}

@Override
public boolean equals(Object other) {
return other instanceof Ingredient && this.amount == ((Ingredient) other).amount() && this.unit.equals(((Ingredient) other).unit()) && this.name.equals(((Ingredient) other).name());
}

@Override
public String toString() {
return "[Ingredient: " + amount + ", " + unit + ", " + name + "]";
}

@Override
public int hashCode() {
return (int) amount >> 2 + name.length();
}
}
36 changes: 27 additions & 9 deletions src/main/java/shef/data/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.logging.*;
import java.util.Iterator;
import com.google.appengine.api.datastore.Entity;
Expand All @@ -32,8 +33,12 @@ public class Recipe {
private String key;
private String name;
private String description;
private double time;
private double servings;
private String imageKey;
private Set<String> tags;
private Set<String> ingredients;
private Set<Ingredient> ingredients;
private Set<String> equipment;
private List<Step> steps;
private Set<SpinOff> spinOffs;
private long timestamp;
Expand All @@ -52,7 +57,7 @@ public Recipe(Recipe recipe) {
}

/** Default constructor called when creating a new recipe. */
public Recipe(String name, String description, Set<String> tags, Set<String> ingredients, List<Step> steps, long timestamp) {
public Recipe(String name, String description, Set<String> tags, Set<Ingredient> ingredients, List<Step> steps, long timestamp) {
this.name = name;
this.description = description;
this.tags = tags;
Expand All @@ -66,14 +71,18 @@ public Recipe(String name, String description, Set<String> tags, Set<String> ing
public Recipe(Entity recipeEntity) {
this.name = (String) recipeEntity.getProperty("name");
this.description = (String) recipeEntity.getProperty("description");
this.time = (double) recipeEntity.getProperty("time");
this.servings = (double) recipeEntity.getProperty("servings");
this.imageKey = (String) recipeEntity.getProperty("imageKey");
this.tags = getTagsFromEntity((Collection<EmbeddedEntity>) recipeEntity.getProperty("tags"));
this.ingredients = getIngredientsFromEntity((Collection<EmbeddedEntity>) recipeEntity.getProperty("ingredients"));
this.equipment = getEquipmentFromEntity((Collection<EmbeddedEntity>) recipeEntity.getProperty("equipment"));
this.steps = getStepsFromEntity((Collection<EmbeddedEntity>) recipeEntity.getProperty("steps"));
this.timestamp = (long) recipeEntity.getProperty("timestamp");
}

/** Constructor called when creating a recipe to display on the recipe feed. */
public Recipe(String key, String name, String description, Set<String> tags, Set<String> ingredients, List<Step> steps, long timestamp) {
public Recipe(String key, String name, String description, Set<String> tags, Set<Ingredient> ingredients, List<Step> steps, long timestamp) {
this.key = key;
this.name = name;
this.tags = tags;
Expand Down Expand Up @@ -112,7 +121,7 @@ public Set<String> getTags() {
}

/** Gets the recipe's ingredients. */
public Set<String> getIngredients() {
public Set<Ingredient> getIngredients() {
return ingredients;
}

Expand All @@ -127,7 +136,7 @@ public void addTag(String tag) {
}

/** Adds an ingredient to the recipe. */
public void addIngredient(String ingredient) {
public void addIngredient(Ingredient ingredient) {
ingredients.add(ingredient);
}

Expand All @@ -142,7 +151,7 @@ public void removeTag(String tag) {
}

/** Removes an ingredient from the recipe. */
public void removeIngredient(String ingredient) {
public void removeIngredient(Ingredient ingredient) {
ingredients.remove(ingredient);
}

Expand Down Expand Up @@ -244,14 +253,23 @@ private Set<String> getTagsFromEntity(Collection<EmbeddedEntity> entityTags) {
}

/** Returns the ingredients of an EmbeddedEntity as a Set. */
private Set<String> getIngredientsFromEntity(Collection<EmbeddedEntity> entityIngredients) {
Set<String> ingredientsSet = new HashSet<>();
private Set<Ingredient> getIngredientsFromEntity(Collection<EmbeddedEntity> entityIngredients) {
Set<Ingredient> ingredientsSet = new HashSet<>();
for (EmbeddedEntity ingredient : entityIngredients) {
ingredientsSet.add((String) ingredient.getProperty("ingredient"));
ingredientsSet.add(new Ingredient(ingredient));
}
return ingredientsSet;
}

/** Returns the equipment of an EmbeddedEntity as a Set. */
private Set<String> getEquipmentFromEntity(Collection<EmbeddedEntity> entityEquipment) {
Set<String> equipmentSet = new HashSet<>();
for (EmbeddedEntity equipment : entityEquipment) {
equipmentSet.add((String) equipment.getProperty("equipment"));
}
return equipmentSet;
}

/** Returns the steps of an EmbeddedEntity as a List. */
private List<Step> getStepsFromEntity(Collection<EmbeddedEntity> entitySteps) {
List<Step> stepsList = new LinkedList<>();
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/shef/servlets/BlobServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;

/** Servlet that serves blobstore files. */
@WebServlet("/blob")
Expand All @@ -33,4 +37,30 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
BlobKey blobKey = new BlobKey(request.getParameter("blob-key"));
blobstoreService.serve(blobKey, response);
}

/*
* Returns the String representation of the BlobKey of the uploaded file, or null if the user didn't upload a file.
* Used in alternative to getting the blob's direct URL, which causes crashes on the live server.
*/
public static String getUploadedFileBlobKey(HttpServletRequest request, String formInputElementName) {
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(request);
List<BlobKey> blobKeys = blobs.get(formInputElementName);

// User submitted form without selecting a file, so we can't get a URL. (dev server)
if (blobKeys == null || blobKeys.isEmpty()) {
return null;
}

// Our form only contains a single file input, so get the first index.
BlobKey blobKey = blobKeys.get(0);

// User submitted form without selecting a file, so we can't get a URL. (live server)
BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
if (blobInfo.getSize() == 0) {
blobstoreService.delete(blobKey);
return null;
}
return blobKey.getKeyString();
}
}
24 changes: 1 addition & 23 deletions src/main/java/shef/servlets/DisplayRecipesServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,34 +53,12 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro

List<Recipe> recipes = new ArrayList<>();
for (Entity entity : results.asIterable()) {
recipes.add(entityToRecipe(entity));
recipes.add(new Recipe(entity));
}

Gson gson = new Gson();

response.setContentType("application/json;");
response.getWriter().println(gson.toJson(recipes));
}

/** Converts a Datastore entity into a Recipe. */
public Recipe entityToRecipe(Entity recipeEntity) {
String key = KeyFactory.keyToString(recipeEntity.getKey());
String name = (String) recipeEntity.getProperty("name");
String description = (String) recipeEntity.getProperty("description");
LinkedHashSet<String> tags = new LinkedHashSet<>((LinkedList<String>) (LinkedList<?>) getDataAsList(recipeEntity.getProperty("tags"), "tag"));
LinkedHashSet<String> ingredients = new LinkedHashSet<>((LinkedList<String>) (LinkedList<?>) getDataAsList(recipeEntity.getProperty("ingredients"), "ingredient"));
LinkedList<Step> steps = (LinkedList<Step>) (LinkedList<?>) getDataAsList(recipeEntity.getProperty("steps"), "step");
long timestamp = (long) recipeEntity.getProperty("timestamp");
return new Recipe(key, name, description, tags, ingredients, steps, timestamp);
}

/** Gets a list of Recipe parameters from a Datastore property. */
public Collection<Object> getDataAsList(Object propertiesObject, String field) {
Collection<EmbeddedEntity> properties = (Collection<EmbeddedEntity>) propertiesObject;
Collection<Object> dataAsList = new LinkedList<>();
for (EmbeddedEntity property : properties) {
dataAsList.add(property.getProperty(field));
}
return dataAsList;
}
}
40 changes: 34 additions & 6 deletions src/main/java/shef/servlets/NewRecipeServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class NewRecipeServlet extends HttpServlet {
private DatastoreService datastore;
private final String TAG = "tag";
private final String INGREDIENT = "ingredient";
private final String EQUIPMENT = "equipment";
private final String STEP = "step";

@Override
Expand All @@ -62,7 +63,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
}
Recipe original = new Recipe(recipeEntity);
response.setContentType("application/json;");
response.getWriter().println(convertToJsonUsingGson(original));
response.getWriter().println(new Gson.toJson(original));
}

/** Posts a new recipe to the servlet. */
Expand All @@ -71,9 +72,13 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) thr
Collection<String> searchStrings = new HashSet<>();
String name = request.getParameter("name");
searchStrings.add(name.toUpperCase());
double time = Double.parseDouble(request.getParameter("time"));
double servings = Double.parseDouble(request.getParameter("servings"));
String imageKey = BlobServlet.getUploadedFileBlobKey(request, "image");
String description = request.getParameter("description");
Collection<EmbeddedEntity> tags = getParameters(request, TAG, searchStrings);
Collection<EmbeddedEntity> ingredients = getParameters(request, INGREDIENT, searchStrings);
Collection<EmbeddedEntity> equipment = getParameters(request, EQUIPMENT, null);
Collection<EmbeddedEntity> steps = getParameters(request, STEP, null);
int likes;
try {
Expand All @@ -85,9 +90,13 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) thr

Entity recipe = new Entity("Recipe");
recipe.setProperty("name", name);
recipe.setProperty("time", time);
recipe.setProperty("servings", servings);
recipe.setProperty("imageKey", imageKey);
recipe.setProperty("description", description);
recipe.setProperty("tags", tags);
recipe.setProperty("ingredients", ingredients);
recipe.setProperty("equipment", equipment);
recipe.setProperty("steps", steps);
recipe.setProperty("search-strings", new ArrayList<String>(searchStrings));
recipe.setProperty("timestamp", timestamp);
Expand Down Expand Up @@ -122,16 +131,35 @@ private Collection<EmbeddedEntity> getParameters(HttpServletRequest request, Str
return parameters;
}

private Collection<EmbeddedEntity> getIngredients(HttpServletRequest request, Collection<String> searchStrings) {
Collection<EmbeddedEntity> parameters = new LinkedList<>();
int parameterNum = 0;
String parameterName = "ingredient" + parameterNum;
String[] parameterValues = request.getParameterValues(parameterName);
for (int i = 0; i < parameterValues.length; i++)
System.out.println(parameterValues[i]);

// In the HTML form, parameters are named as [field name][index], ie step0.
// This loop increments the index of the parameter's name, exiting once it reaches an index for which there is no parameter.
while (parameterValues != null) {
addToSearchStrings(searchStrings, parameterValues[2]);
EmbeddedEntity parameterEntity = new EmbeddedEntity();
parameterEntity.setProperty("amount", Double.parseDouble(parameterValues[0]));
parameterEntity.setProperty("unit", parameterValues[1]);
parameterEntity.setProperty("name", parameterValues[2]);
parameters.add(parameterEntity);

parameterName = "ingredient" + (++parameterNum);
parameterValues = request.getParameterValues(parameterName);
}
return parameters;
}

/** Adds a formatted search string to the set of search strings. */
private void addToSearchStrings(Collection<String> searchStrings, String stringToAdd) {
if (searchStrings == null) {
return;
}
searchStrings.add(stringToAdd.toUpperCase());
}

private String convertToJsonUsingGson(Recipe recipe) {
Gson gson = new Gson();
return gson.toJson(recipe);
}
}
45 changes: 45 additions & 0 deletions src/main/java/shef/servlets/RecipeImageUploadUrlServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shef.servlets;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ServingUrlOptions;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.List;

/** Gets the Blobstore URL to send the recipe creation form to. */
@WebServlet("/recipe-image-upload-url")
public class RecipeImageUploadUrlServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
String uploadUrl = blobstoreService.createUploadUrl("/new-recipe");
response.setContentType("text/html");
response.getWriter().println(uploadUrl);
}
}
Loading