Editable shopping categories
This commit is contained in:
parent
349fec17ac
commit
3fd7bdb842
121
recipes.sql
Normal file
121
recipes.sql
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
CREATE TABLE keylookup (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
word TEXT,
|
||||||
|
item TEXT,
|
||||||
|
ingkey TEXT,
|
||||||
|
count INTEGER,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE info (
|
||||||
|
version_super INTEGER,
|
||||||
|
version_major INTEGER,
|
||||||
|
version_minor INTEGER,
|
||||||
|
last_access INTEGER,
|
||||||
|
rowid INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (rowid)
|
||||||
|
);
|
||||||
|
CREATE TABLE recipe (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
title TEXT,
|
||||||
|
instructions TEXT,
|
||||||
|
modifications TEXT,
|
||||||
|
cuisine TEXT,
|
||||||
|
rating INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
source TEXT,
|
||||||
|
preptime INTEGER,
|
||||||
|
cooktime INTEGER,
|
||||||
|
servings FLOAT,
|
||||||
|
yields FLOAT,
|
||||||
|
yield_unit VARCHAR(32),
|
||||||
|
image BLOB,
|
||||||
|
thumb BLOB,
|
||||||
|
deleted BOOLEAN,
|
||||||
|
recipe_hash VARCHAR(32),
|
||||||
|
ingredient_hash VARCHAR(32),
|
||||||
|
link TEXT,
|
||||||
|
last_modified INTEGER,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CHECK (deleted IN (0, 1))
|
||||||
|
);
|
||||||
|
CREATE TABLE plugin_info (
|
||||||
|
plugin TEXT,
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
version_super INTEGER,
|
||||||
|
version_major INTEGER,
|
||||||
|
version_minor INTEGER,
|
||||||
|
plugin_version VARCHAR(32),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE categories (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
recipe_id INTEGER,
|
||||||
|
category TEXT,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(recipe_id) REFERENCES recipe (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE ingredients (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
recipe_id INTEGER,
|
||||||
|
refid INTEGER,
|
||||||
|
unit TEXT,
|
||||||
|
amount FLOAT,
|
||||||
|
rangeamount FLOAT,
|
||||||
|
item TEXT,
|
||||||
|
ingkey TEXT,
|
||||||
|
optional BOOLEAN,
|
||||||
|
shopoptional INTEGER,
|
||||||
|
inggroup TEXT,
|
||||||
|
position INTEGER,
|
||||||
|
deleted BOOLEAN,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CHECK (deleted IN (0, 1)),
|
||||||
|
FOREIGN KEY(recipe_id) REFERENCES recipe (id),
|
||||||
|
FOREIGN KEY(refid) REFERENCES recipe (id),
|
||||||
|
CHECK (optional IN (0, 1))
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "pantry" (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
ingkey TEXT(32),
|
||||||
|
pantry BOOLEAN,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CHECK (pantry IN (0, 1))
|
||||||
|
);
|
||||||
|
CREATE TABLE unitdict (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
ukey VARCHAR(150),
|
||||||
|
value VARCHAR(150),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE crossunitdict (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
cukey VARCHAR(150),
|
||||||
|
value VARCHAR(150),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE density (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
dkey VARCHAR(150),
|
||||||
|
value VARCHAR(150),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE shopcatsorder (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
shopcategory TEXT(32),
|
||||||
|
position INTEGER,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE convtable (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
ckey VARCHAR(150),
|
||||||
|
value VARCHAR(150),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "shopcats" (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
ingkey TEXT(32),
|
||||||
|
shopcategory TEXT,
|
||||||
|
position INTEGER,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "recipe_ingredients" ("Recipe_id" integer not null, "ingredientHash_id" integer not null, unique ("ingredientHash_id"));
|
|
@ -106,12 +106,10 @@ public class AdminMainBean implements Serializable {
|
||||||
if (suggestionList == null) {
|
if (suggestionList == null) {
|
||||||
switch (this.userSession.getSearchType()) {
|
switch (this.userSession.getSearchType()) {
|
||||||
case rst_BY_CATEGORY:
|
case rst_BY_CATEGORY:
|
||||||
suggestionList =
|
suggestionList = recipeService.findCategories();
|
||||||
recipeService.findCategories();
|
|
||||||
break;
|
break;
|
||||||
case rst_BY_CUISINE:
|
case rst_BY_CUISINE:
|
||||||
suggestionList =
|
suggestionList = recipeService.findCuisines();
|
||||||
recipeService.findCuisines();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
suggestionList = new ArrayList<String>(1);
|
suggestionList = new ArrayList<String>(1);
|
||||||
|
@ -220,6 +218,14 @@ public class AdminMainBean implements Serializable {
|
||||||
return "detailEdit?faces-redirect=true";
|
return "detailEdit?faces-redirect=true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to "More features" page (shopping list and
|
||||||
|
* maint.)
|
||||||
|
*/
|
||||||
|
public String doMore() {
|
||||||
|
return "shoppingList.jsf";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show selected recipe
|
* Show selected recipe
|
||||||
*
|
*
|
||||||
|
|
|
@ -243,6 +243,9 @@ public class RecipeDetailBean implements Serializable {
|
||||||
buildIngredientFacade(recipe.getIngredientHash()));
|
buildIngredientFacade(recipe.getIngredientHash()));
|
||||||
stringifyCategories(recipe);
|
stringifyCategories(recipe);
|
||||||
|
|
||||||
|
this.shop = this.getUserSession().getShoppingList()
|
||||||
|
.contains(recipe);
|
||||||
|
|
||||||
log.info("Set recipe: " + this.recipe);
|
log.info("Set recipe: " + this.recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,8 +265,8 @@ public class RecipeDetailBean implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Categories are a Set attached to recipe. Build
|
* Categories are a Set attached to recipe. Build a
|
||||||
* a displayable comma-separated list of them.
|
* displayable comma-separated list of them.
|
||||||
*
|
*
|
||||||
* @param recipe Recipe to get categories from.
|
* @param recipe Recipe to get categories from.
|
||||||
*
|
*
|
||||||
|
@ -667,6 +670,26 @@ public class RecipeDetailBean implements Serializable {
|
||||||
.getWrappedData();
|
.getWrappedData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shop = false;
|
||||||
|
|
||||||
|
public boolean isShop() {
|
||||||
|
return shop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add/remove recipe to shopping list (toggle)
|
||||||
|
*/
|
||||||
|
public void doShop() {
|
||||||
|
shop = !shop;
|
||||||
|
List<Recipe> shoppingList =
|
||||||
|
userSession.getShoppingList();
|
||||||
|
if (shop) {
|
||||||
|
shoppingList.add(recipe);
|
||||||
|
} else {
|
||||||
|
shoppingList.remove(recipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the recipe.
|
* Save the recipe.
|
||||||
*
|
*
|
||||||
|
|
161
src/main/java/com/mousetech/gourmetj/ShopIngredient.java
Normal file
161
src/main/java/com/mousetech/gourmetj/ShopIngredient.java
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
|
import com.mousetech.gourmetj.utils.IngredientDigester;
|
||||||
|
import com.mousetech.gourmetj.utils.IngredientDigester.IngredientAmountFormat;
|
||||||
|
|
||||||
|
public class ShopIngredient implements Comparable<Object> {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param shopCat
|
||||||
|
* @param ingkey
|
||||||
|
*/
|
||||||
|
public ShopIngredient( String shopCat, String item,
|
||||||
|
String ingkey) {
|
||||||
|
this.shopCat = shopCat;
|
||||||
|
this.ingkey = ingkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param amount
|
||||||
|
* @param unit
|
||||||
|
* @param item
|
||||||
|
* @param ingkey
|
||||||
|
* @param shopCat
|
||||||
|
*/
|
||||||
|
public ShopIngredient( double amount, String unit,
|
||||||
|
String item, String ingkey,
|
||||||
|
String shopCat) {
|
||||||
|
this.amount = amount;
|
||||||
|
this.unit = unit;
|
||||||
|
this.item = item;
|
||||||
|
this.ingkey = ingkey;
|
||||||
|
this.shopCat = shopCat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String shopCat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the shopCat
|
||||||
|
*/
|
||||||
|
public String getShopCat() {
|
||||||
|
return shopCat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param shopCat the shopCat to set
|
||||||
|
*/
|
||||||
|
public void setShopCat(String shopCat) {
|
||||||
|
this.shopCat = shopCat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the ingkey
|
||||||
|
*/
|
||||||
|
public String getIngkey() {
|
||||||
|
return ingkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ingkey the ingkey to set
|
||||||
|
*/
|
||||||
|
public void setIngkey(String ingkey) {
|
||||||
|
this.ingkey = ingkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the amount
|
||||||
|
*/
|
||||||
|
public double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param amount the amount to set
|
||||||
|
*/
|
||||||
|
public void setAmount(double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the displayAmount
|
||||||
|
*/
|
||||||
|
public String getDisplayAmount() {
|
||||||
|
return IngredientDigester.displayAmount(
|
||||||
|
IngredientAmountFormat.IA_TEXT, this.getAmount(),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the unit
|
||||||
|
*/
|
||||||
|
public String getUnit() {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param unit the unit to set
|
||||||
|
*/
|
||||||
|
public void setUnit(String unit) {
|
||||||
|
this.unit = unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the item
|
||||||
|
*/
|
||||||
|
public String getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ingkey;
|
||||||
|
private double amount;
|
||||||
|
private String displayAmount;
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
if ((o == null) || !(o instanceof ShopIngredient)) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Invalid shipIngredient comparison");
|
||||||
|
}
|
||||||
|
ShopIngredient o1 = (ShopIngredient) o;
|
||||||
|
int i = relate(this.getItem(), o1.getItem());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i = relate(this.getShopCat(), o1.getShopCat());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// TODO: normalize case, singular/plural/abbreviations
|
||||||
|
i = relate(this.getUnit(), o1.getUnit());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return i; // ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
private int relate(String item2, String item3) {
|
||||||
|
if ((item2 == null) && (item3 == null)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (item2 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (item3 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return item2.compareTo(item3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getAmount() + " " + this.getUnit() + " "
|
||||||
|
+ this.getItem() + " " + this.getIngkey() + " "
|
||||||
|
+ this.getShopCat();
|
||||||
|
}
|
||||||
|
}
|
213
src/main/java/com/mousetech/gourmetj/ShoppingListBean.java
Normal file
213
src/main/java/com/mousetech/gourmetj/ShoppingListBean.java
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.faces.event.AjaxBehaviorEvent;
|
||||||
|
import javax.faces.view.ViewScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.primefaces.event.ReorderEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.dao.ShopcatRepository;
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Ingredient;
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Shopcat;
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@ViewScoped
|
||||||
|
public class ShoppingListBean implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial version for session save/restore
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID =
|
||||||
|
7449440266704831598L;
|
||||||
|
|
||||||
|
/* Logger */
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static final Logger log =
|
||||||
|
LoggerFactory.getLogger(ShoppingListBean.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private UserSession userSession;
|
||||||
|
|
||||||
|
private List<ShopIngredient> siList;
|
||||||
|
|
||||||
|
// private List<ShopIngredient> ingredientList;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// Load up details on recipes
|
||||||
|
this.siList = new ArrayList<ShopIngredient>(30);
|
||||||
|
buildMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Recipe> getRecipeList() {
|
||||||
|
return this.userSession.getShoppingList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ShopIngredient> getIngredientList() {
|
||||||
|
return this.siList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildMaps() {
|
||||||
|
for (Recipe r : this.getRecipeList()) {
|
||||||
|
buildMapsFor(r);
|
||||||
|
}
|
||||||
|
// Now consolidate amounts and sort by
|
||||||
|
// shopcat/item/ingkey
|
||||||
|
optimizeIngredients(this.siList);
|
||||||
|
|
||||||
|
this.siList.sort(new ShopclassComparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the ingredient list for the selected recipe and add
|
||||||
|
* them to siList
|
||||||
|
*
|
||||||
|
* @param r
|
||||||
|
*
|
||||||
|
* @see #buildMaps()
|
||||||
|
*/
|
||||||
|
private void buildMapsFor(Recipe r) {
|
||||||
|
for (Ingredient ing : r.getIngredientHash()) {
|
||||||
|
String ingkey = ing.getIngkey();
|
||||||
|
if (StringUtils.isBlank(ingkey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String shopCatName = ing.getShopCat() != null
|
||||||
|
? ing.getShopCat().getShopcategory()
|
||||||
|
: null;
|
||||||
|
ShopIngredient sing = new ShopIngredient(
|
||||||
|
ing.getAmount(), ing.getUnit(),
|
||||||
|
ing.getItem(), ing.getIngkey(), shopCatName);
|
||||||
|
siList.add(sing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort ShopIngredient list, then optimize it by
|
||||||
|
* consolidating amounts where possible.
|
||||||
|
*
|
||||||
|
* @param victim List to optimize
|
||||||
|
*
|
||||||
|
* #TestedBy @see ShoppingListBeanTest
|
||||||
|
*/
|
||||||
|
static void optimizeIngredients(
|
||||||
|
List<ShopIngredient> victim) {
|
||||||
|
victim.sort(null);
|
||||||
|
for (int i = 0; i < (victim.size() - 1); i++) {
|
||||||
|
ShopIngredient si = victim.get(i);
|
||||||
|
ShopIngredient si2 = victim.get(i + 1);
|
||||||
|
if (si.compareTo(si2) == 0) {
|
||||||
|
si.setAmount(si.getAmount() + si2.getAmount());
|
||||||
|
victim.remove(si2); // reduces size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShopclassComparator
|
||||||
|
implements Comparator<ShopIngredient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(ShopIngredient ing1,
|
||||||
|
ShopIngredient ing2) {
|
||||||
|
int i = 0;
|
||||||
|
i = relate(ing1.getShopCat(), ing2.getShopCat());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i = relate(ing1.getItem(), ing2.getItem());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i = relate(ing1.getIngkey(), ing2.getIngkey());
|
||||||
|
if (i != 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int relate(String item2, String item3) {
|
||||||
|
if ((item2 == null) && (item3 == null)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (item2 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (item3 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return item2.compareTo(item3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Shopcat> shopcatList;
|
||||||
|
|
||||||
|
public List<Shopcat> getShopcatList() {
|
||||||
|
if (shopcatList == null) {
|
||||||
|
shopcatList = loadShopcatList();
|
||||||
|
}
|
||||||
|
return shopcatList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ShopcatRepository shopcatRepository;
|
||||||
|
|
||||||
|
private List<Shopcat> loadShopcatList() {
|
||||||
|
return shopcatRepository
|
||||||
|
.findAllByOrderByShopcategoryAsc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Shopcat xeditShopcat = new Shopcat();
|
||||||
|
|
||||||
|
private String oldShopcategoryName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the editShopcat
|
||||||
|
*/
|
||||||
|
public Shopcat getEditShopcat() {
|
||||||
|
return xeditShopcat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doEditShopcat(int scId) {
|
||||||
|
xeditShopcat = null;
|
||||||
|
final List<Shopcat> scl = getShopcatList();
|
||||||
|
for (Shopcat sc : scl) {
|
||||||
|
if (sc.getId() == scId) {
|
||||||
|
xeditShopcat = sc;
|
||||||
|
this.oldShopcategoryName = sc.getShopcategory();
|
||||||
|
if (sc.getPosition() == null) {
|
||||||
|
sc.setPosition(0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.error("SHOPCAT " + scId + " NOT FOUND");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ajaxOnClickShopcatIngkey() {
|
||||||
|
// Saves 1 shopcat/ingkey
|
||||||
|
this.shopcatRepository.save(xeditShopcat);
|
||||||
|
this.shopcatList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all ingredient keys for shopcat name-change.
|
||||||
|
* Note that once done, this cannot be undone!
|
||||||
|
*/
|
||||||
|
public void ajaxOnClickShopcat() {
|
||||||
|
this.shopcatRepository.UpdateShopcats(
|
||||||
|
this.oldShopcategoryName, xeditShopcat.getShopcategory());
|
||||||
|
this.shopcatList = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -182,6 +182,13 @@ public class UserSession implements Serializable {
|
||||||
// Session timeout, ms (25 minutes)
|
// Session timeout, ms (25 minutes)
|
||||||
long sessionTimeoutInterval = 25 * 60_000L;
|
long sessionTimeoutInterval = 25 * 60_000L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When you click the "shop" button on a recipe, it
|
||||||
|
* gets added to this list. Note that it's the fully-expanded
|
||||||
|
* recipe!
|
||||||
|
*/
|
||||||
|
private List<Recipe> shoppingList = new ArrayList<Recipe>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the sessionTimeoutInterval
|
* @return the sessionTimeoutInterval
|
||||||
*/
|
*/
|
||||||
|
@ -199,4 +206,8 @@ public class UserSession implements Serializable {
|
||||||
log.warn("Session Idle listener logout");
|
log.warn("Session Idle listener logout");
|
||||||
return "/main.jsf";
|
return "/main.jsf";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Recipe> getShoppingList() {
|
||||||
|
return this.shoppingList ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@ package com.mousetech.gourmetj.persistence.dao;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@ -28,7 +31,14 @@ public interface ShopcatRepository
|
||||||
@Query(value = SQL_FIND_CATEGORIES, nativeQuery = true)
|
@Query(value = SQL_FIND_CATEGORIES, nativeQuery = true)
|
||||||
public List<String> findDistinctCategoryNative();
|
public List<String> findDistinctCategoryNative();
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Query(value = "UPDATE Shopcat set shopcategory = :newCatname WHERE shopcategory = :oldCatname")
|
||||||
|
@Modifying
|
||||||
|
public int UpdateShopcats(String oldCatname, String newCatname);
|
||||||
|
|
||||||
public Shopcat findShopcatByIngkey(String ingkey);
|
public Shopcat findShopcatByIngkey(String ingkey);
|
||||||
|
|
||||||
public void deleteByIngkey(String key);
|
public void deleteByIngkey(String key);
|
||||||
|
|
||||||
|
public List<Shopcat> findAllByOrderByShopcategoryAsc();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<h1>
|
<h1>
|
||||||
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
|
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
|
||||||
</h1>
|
</h1>
|
||||||
|
<p:ajaxStatus onerror="PF('opError').show()"/>
|
||||||
<ui:insert name="content">
|
<ui:insert name="content">
|
||||||
<ui:include src="content.xhtml" />
|
<ui:include src="content.xhtml" />
|
||||||
</ui:insert>
|
</ui:insert>
|
||||||
|
@ -29,10 +30,10 @@
|
||||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||||
>Apache License, Version 2.0</a>.
|
>Apache License, Version 2.0</a>.
|
||||||
<p>Based on Gourmet Recipe Manager by T. Hinkle</p>
|
<p>Based on Gourmet Recipe Manager by T. Hinkle</p>
|
||||||
<h:form id="ftmTimeout">
|
<h:form id="frmTimeout">
|
||||||
<p:idleMonitor
|
<p:idleMonitor
|
||||||
timeout="#{userSession.sessionTimeoutInterval}"
|
timeout="#{userSession.sessionTimeoutInterval}"
|
||||||
onidle="PF('sessionExpiredConfirmation').show()"
|
onidle="PF('dlgSessionExpired').show()"
|
||||||
>
|
>
|
||||||
<p:ajax event="idle"
|
<p:ajax event="idle"
|
||||||
listener="#{userSession.sessionIdleListener}"
|
listener="#{userSession.sessionIdleListener}"
|
||||||
|
@ -43,12 +44,12 @@
|
||||||
message="Your session has expired."
|
message="Your session has expired."
|
||||||
header="#{msgs['confirmDialog.initiatingDestroyProcess.label']}"
|
header="#{msgs['confirmDialog.initiatingDestroyProcess.label']}"
|
||||||
severity="alert"
|
severity="alert"
|
||||||
widgetVar="sessionExpiredConfirmation"
|
widgetVar="dlgSessionExpired"
|
||||||
style="z-index: 25000"
|
style="z-index: 25000"
|
||||||
>
|
>
|
||||||
<p:commandButton id="cmdExpiredOK"
|
<p:commandButton id="cmdExpiredOK"
|
||||||
value="OK" action="/main.jsf"
|
value="OK" action="/main.jsf"
|
||||||
oncomplete="PF('sessionExpiredConfirmation').hide()"
|
oncomplete="PF('dlgSessionExpired').hide()"
|
||||||
/>
|
/>
|
||||||
</p:confirmDialog>
|
</p:confirmDialog>
|
||||||
</h:form>
|
</h:form>
|
||||||
|
@ -60,7 +61,7 @@
|
||||||
severity="alert" widgetVar="opError"
|
severity="alert" widgetVar="opError"
|
||||||
>
|
>
|
||||||
<p:commandButton value="OK"
|
<p:commandButton value="OK"
|
||||||
oncomplete="PF('cd').hide()"
|
oncomplete="PF('opError').hide()"
|
||||||
/>
|
/>
|
||||||
</p:confirmDialog>
|
</p:confirmDialog>
|
||||||
</h:form>
|
</h:form>
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.greenButton {
|
||||||
|
background-color: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
font-family: 'latoregular', 'Trebuchet MS,Arial,Helvetica,sans-serif';
|
font-family: 'latoregular', 'Trebuchet MS,Arial,Helvetica,sans-serif';
|
||||||
font-size: 1em
|
font-size: 1em
|
||||||
|
|
68
src/main/resources/META-INF/resources/editShopcatList.xhtml
Normal file
68
src/main/resources/META-INF/resources/editShopcatList.xhtml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||||
|
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||||
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||||
|
xmlns:p="http://primefaces.org/ui"
|
||||||
|
>
|
||||||
|
<h:head>
|
||||||
|
<title>Shopping Category</title>
|
||||||
|
<style type="text/css">
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</h:head>
|
||||||
|
<h:body>
|
||||||
|
<ui:component>
|
||||||
|
<h:form id="frmShopcat">
|
||||||
|
<p:panelGrid columns="1">
|
||||||
|
<p:dataTable id="tblShopcats"
|
||||||
|
style="width: 600px"
|
||||||
|
value="#{shoppingListBean.shopcatList}"
|
||||||
|
sortBy="#{item.shopCat}" var="item"
|
||||||
|
>
|
||||||
|
<p:headerRow>
|
||||||
|
<p:column colspan="4">
|
||||||
|
<h:outputText value="#{item.shopCat}" />
|
||||||
|
</p:column>
|
||||||
|
</p:headerRow>
|
||||||
|
</p:dataTable>
|
||||||
|
<div>Ingredient key:
|
||||||
|
#{editShopcatBean.ingkey}</div>
|
||||||
|
<p:outputLabel for="@next"
|
||||||
|
value="Category Name"
|
||||||
|
/>
|
||||||
|
<p:inputText id="ctlShopcat"
|
||||||
|
value="#{editShopcatBean.shopcatName}"
|
||||||
|
>
|
||||||
|
</p:inputText>
|
||||||
|
<h:outputText value="suggestion:" />
|
||||||
|
<p:selectOneMenu id="ctlShopcatMenu"
|
||||||
|
value="#{editShopcatBean.shopcatSuggestion}"
|
||||||
|
>
|
||||||
|
<f:selectItems
|
||||||
|
value="#{editShopcatBean.shopcatSuggestionList}"
|
||||||
|
/>
|
||||||
|
<p:ajax event="change"
|
||||||
|
listener="#{editShopcatBean.ajaxShopcatSuggest}"
|
||||||
|
update="ctlShopcat"
|
||||||
|
/>
|
||||||
|
</p:selectOneMenu>
|
||||||
|
<p:panelGrid columns="2" style="width: 100%">
|
||||||
|
<p:commandButton id="scDlgOK" value="OK"
|
||||||
|
style="width: 6em"
|
||||||
|
action="#{recipeDetailBean.doUpdateShopcat}"
|
||||||
|
update="form1:tabGroupClient:ingredientTable"
|
||||||
|
oncomplete="PF('editShopcatDlg').hide()"
|
||||||
|
/>
|
||||||
|
<p:commandButton id="scDlgCan"
|
||||||
|
value="Cancel" style="width: 6em"
|
||||||
|
onclick="PF('editShopcatDlg').hide()"
|
||||||
|
/>
|
||||||
|
</p:panelGrid>
|
||||||
|
</p:panelGrid>
|
||||||
|
</h:form>
|
||||||
|
</ui:component>
|
||||||
|
</h:body>
|
||||||
|
</html>
|
|
@ -43,6 +43,9 @@
|
||||||
<p:commandButton value="New Recipe"
|
<p:commandButton value="New Recipe"
|
||||||
action="#{adminMainBean.doNewRecipe}"
|
action="#{adminMainBean.doNewRecipe}"
|
||||||
/>
|
/>
|
||||||
|
<p:commandButton value="More..."
|
||||||
|
action="#{adminMainBean.doMore}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</h:form>
|
</h:form>
|
||||||
<h:form id="form2">
|
<h:form id="form2">
|
||||||
|
|
|
@ -57,6 +57,15 @@
|
||||||
styleClass="ui-button-print"
|
styleClass="ui-button-print"
|
||||||
immediate="true"
|
immediate="true"
|
||||||
/>
|
/>
|
||||||
|
<p:commandButton id="ctlShop"
|
||||||
|
icon="ui-icon-cart"
|
||||||
|
value="Shop"
|
||||||
|
immediate="true"
|
||||||
|
styleClass="#{recipeDetailBean.shop ? 'greenButton' : null}"
|
||||||
|
action="#{recipeDetailBean.doShop}"
|
||||||
|
update="ctlShop"
|
||||||
|
/>
|
||||||
|
<h:outputText value=""/>
|
||||||
<p:outputLabel for="@next"
|
<p:outputLabel for="@next"
|
||||||
value="Categories:"
|
value="Categories:"
|
||||||
/>
|
/>
|
||||||
|
@ -197,8 +206,7 @@
|
||||||
<p:confirmDialog closable="false" id="okDeleteDlg"
|
<p:confirmDialog closable="false" id="okDeleteDlg"
|
||||||
header="Confirm Deletion"
|
header="Confirm Deletion"
|
||||||
message="OK to delete this recipe?"
|
message="OK to delete this recipe?"
|
||||||
severity="alert"
|
severity="alert" widgetVar="okDeleteDlg"
|
||||||
widgetVar="okDeleteDlg"
|
|
||||||
style="z-index: 25000"
|
style="z-index: 25000"
|
||||||
>
|
>
|
||||||
<p:commandButton id="dlgOK" value="OK"
|
<p:commandButton id="dlgOK" value="OK"
|
||||||
|
|
198
src/main/resources/META-INF/resources/shoppingList.xhtml
Normal file
198
src/main/resources/META-INF/resources/shoppingList.xhtml
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
||||||
|
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||||
|
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||||
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||||
|
xmlns:p="http://primefaces.org/ui"
|
||||||
|
xmlns:c="http://xmlns.jcp.org/jstl"
|
||||||
|
>
|
||||||
|
<ui:define name="title">Gourmet Recipe Manager - Shopping</ui:define>
|
||||||
|
<ui:define name="content">
|
||||||
|
<style>
|
||||||
|
.recipeTitle {
|
||||||
|
font-size: larger;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: large;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-panelgrid-cell {
|
||||||
|
border-width: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h:messages />
|
||||||
|
<h:form id="form1">
|
||||||
|
<p:dataTable id="tblRecipes" style="width: 600px"
|
||||||
|
value="#{shoppingListBean.recipeList}" var="item"
|
||||||
|
>
|
||||||
|
<p:column headerText="Recipe">
|
||||||
|
<h:outputText value="#{item.title}" />
|
||||||
|
</p:column>
|
||||||
|
</p:dataTable>
|
||||||
|
<!-- ====== Ingredients ============================ -->
|
||||||
|
<p:column id="ingredientsc"
|
||||||
|
style="width: 25%; vertical-align: top;"
|
||||||
|
>
|
||||||
|
<p:dataTable id="tblShopIngredients"
|
||||||
|
style="width: 600px; margin-top: 10px"
|
||||||
|
value="#{shoppingListBean.ingredientList}"
|
||||||
|
sortBy="#{item.shopCat}" var="item"
|
||||||
|
>
|
||||||
|
<f:facet name="header">
|
||||||
|
<h:outputText styleClass="subtitle"
|
||||||
|
value="Ingredients"
|
||||||
|
/>
|
||||||
|
</f:facet>
|
||||||
|
<p:headerRow>
|
||||||
|
<p:column colspan="4">
|
||||||
|
<h:outputText value="#{item.shopCat}" />
|
||||||
|
</p:column>
|
||||||
|
</p:headerRow>
|
||||||
|
<p:column label="ingKey">
|
||||||
|
<h:outputText value="#{item.ingkey}" />
|
||||||
|
</p:column>
|
||||||
|
<p:column label="Amt"
|
||||||
|
style="width: 3em; text-align: right"
|
||||||
|
>
|
||||||
|
<h:outputText
|
||||||
|
value="#{item.displayAmount}"
|
||||||
|
/>
|
||||||
|
</p:column>
|
||||||
|
<p:column label="Units" style="width: 5em">
|
||||||
|
<h:outputText value="#{item.unit}" />
|
||||||
|
</p:column>
|
||||||
|
<p:column label="Item" style="width: 20em">
|
||||||
|
<h:outputText value="#{item.item}" />
|
||||||
|
</p:column>
|
||||||
|
</p:dataTable>
|
||||||
|
</p:column>
|
||||||
|
<p:commandButton id="doHome" value="Home"
|
||||||
|
icon="ui-icon-home" ajax="false" immediate="true"
|
||||||
|
action="main.jsf"
|
||||||
|
/>
|
||||||
|
<p:dataTable id="tblShopcats" style="width: 300px"
|
||||||
|
value="#{shoppingListBean.shopcatList}"
|
||||||
|
sortBy="#{item.shopcategory}" var="item"
|
||||||
|
>
|
||||||
|
<f:facet name="header">
|
||||||
|
<h:outputText
|
||||||
|
value="Ingredient Keys/Shopping Categories"
|
||||||
|
/>
|
||||||
|
</f:facet>
|
||||||
|
<p:headerRow>
|
||||||
|
<p:column>
|
||||||
|
<h:outputText id="ctlCatname"
|
||||||
|
value="#{item.shopcategory}"
|
||||||
|
/>
|
||||||
|
</p:column>
|
||||||
|
<p:column style="width: 5em">
|
||||||
|
<p:commandButton id="ctlEditCat"
|
||||||
|
value="Rename"
|
||||||
|
action="#{shoppingListBean.doEditShopcat(item.id)}"
|
||||||
|
onclick="PF('dlgEditShopcat').show()"
|
||||||
|
update="frmEscat:pnlData"
|
||||||
|
/>
|
||||||
|
</p:column>
|
||||||
|
</p:headerRow>
|
||||||
|
<p:column>
|
||||||
|
<h:outputText value="#{item.ingkey}"
|
||||||
|
onclick="clickIngkeyName()"
|
||||||
|
/>
|
||||||
|
</p:column>
|
||||||
|
<p:column style="width: 5em">
|
||||||
|
<p:commandButton id="ctlEditIngkey"
|
||||||
|
value="Move"
|
||||||
|
action="#{shoppingListBean.doEditShopcat(item.id)}"
|
||||||
|
onclick="PF('dlgEditIngkey').show()"
|
||||||
|
update="frmIngkey:pnlData"
|
||||||
|
/>
|
||||||
|
</p:column>
|
||||||
|
</p:dataTable>
|
||||||
|
</h:form>
|
||||||
|
<!-- -->
|
||||||
|
<h:form id="frmEscat">
|
||||||
|
<p:dialog id="dlgEditShopcat"
|
||||||
|
widgetVar="dlgEditShopcat"
|
||||||
|
header="Edit/Rename Shopping Category"
|
||||||
|
closeOnEscape="true"
|
||||||
|
>
|
||||||
|
<p:panelGrid id="pnlData" columns="2"
|
||||||
|
styleClass="ui-panelgrid-blank"
|
||||||
|
>
|
||||||
|
<p:outputLabel for="@next"
|
||||||
|
value="Shopping Category"
|
||||||
|
/>
|
||||||
|
<p:inputText id="ctlDlgShopcatName"
|
||||||
|
label="Position" required="true"
|
||||||
|
value="#{shoppingListBean.editShopcat.shopcategory}"
|
||||||
|
/>
|
||||||
|
<p:outputLabel for="@next" value="Position" />
|
||||||
|
<p:inputText id="ctlDlgShopcatPosition"
|
||||||
|
type="number" required="true" size="3"
|
||||||
|
value="#{shoppingListBean.editShopcat.position}"
|
||||||
|
/>
|
||||||
|
</p:panelGrid>
|
||||||
|
<p:panelGrid columns="2" style="width: 100%"
|
||||||
|
styleClass="ui-panelgrid-blank"
|
||||||
|
>
|
||||||
|
<p:commandButton id="scDlgOK" value="OK"
|
||||||
|
style="width: 6em"
|
||||||
|
action="#{shoppingListBean.ajaxOnClickShopcat}"
|
||||||
|
update="form1:tblShopcats"
|
||||||
|
process="@this pnlData"
|
||||||
|
oncomplete="PF('dlgEditShopcat').hide()"
|
||||||
|
/>
|
||||||
|
<p:commandButton id="scDlgCan" value="Cancel"
|
||||||
|
style="width: 6em"
|
||||||
|
onclick="PF('dlgEditShopcat').hide()"
|
||||||
|
/>
|
||||||
|
</p:panelGrid>
|
||||||
|
</p:dialog>
|
||||||
|
</h:form>
|
||||||
|
<!-- -->
|
||||||
|
<h:form id="frmIngkey">
|
||||||
|
<p:dialog id="dlgEditIngkey"
|
||||||
|
widgetVar="dlgEditIngkey"
|
||||||
|
header="Edit/Rename Shopping Category for Ingredient Key"
|
||||||
|
closeOnEscape="true"
|
||||||
|
>
|
||||||
|
<p:panelGrid id="pnlData" columns="2"
|
||||||
|
styleClass="ui-panelgrid-blank"
|
||||||
|
>
|
||||||
|
<p:outputLabel for="@next"
|
||||||
|
value="Ingredient Key"
|
||||||
|
/>
|
||||||
|
<h:outputText id="ctlDlgIngKeyIngkey"
|
||||||
|
value="#{shoppingListBean.editShopcat.ingkey}"
|
||||||
|
/>
|
||||||
|
<p:outputLabel for="@next"
|
||||||
|
value="Shopping Category"
|
||||||
|
/>
|
||||||
|
<p:inputText id="ctlDlgIngkeyShopcatName"
|
||||||
|
label="Position" required="true"
|
||||||
|
value="#{shoppingListBean.editShopcat.shopcategory}"
|
||||||
|
/>
|
||||||
|
</p:panelGrid>
|
||||||
|
<p:panelGrid columns="2" style="width: 100%"
|
||||||
|
styleClass="ui-panelgrid-blank"
|
||||||
|
>
|
||||||
|
<p:commandButton id="ingkDlgOK" value="OK"
|
||||||
|
style="width: 6em"
|
||||||
|
action="#{shoppingListBean.ajaxOnClickShopcatIngkey}"
|
||||||
|
update="form1:tblShopcats"
|
||||||
|
process="@this pnlData"
|
||||||
|
oncomplete="PF('dlgEditIngkey').hide()"
|
||||||
|
/>
|
||||||
|
<p:commandButton id="ingkDlgCan"
|
||||||
|
value="Cancel" style="width: 6em"
|
||||||
|
onclick="PF('dlgEditIngkey').hide()"
|
||||||
|
/>
|
||||||
|
</p:panelGrid>
|
||||||
|
</p:dialog>
|
||||||
|
</h:form>
|
||||||
|
</ui:define>
|
||||||
|
</ui:composition>
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2022, Tim Holloway
|
||||||
|
*
|
||||||
|
* Date written: Jan 12, 2022
|
||||||
|
* Author: Tim Holloway <timh@mousetech.com>
|
||||||
|
*/
|
||||||
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.mousetech.gourmetj.ShopIngredient;
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Shopcat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author timh
|
||||||
|
* @since Jan 12, 2022
|
||||||
|
*/
|
||||||
|
class ShoppingListBeanTest {
|
||||||
|
|
||||||
|
static List<ShopIngredient> testList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws java.lang.Exception
|
||||||
|
*/
|
||||||
|
@BeforeAll
|
||||||
|
static void setUpBeforeClass() throws Exception {
|
||||||
|
testList = new ArrayList<ShopIngredient>();
|
||||||
|
testList.add(new ShopIngredient(2.0d, "cup",
|
||||||
|
"sugar", "sugar", "baking"));
|
||||||
|
testList.add(new ShopIngredient(1.5, "tsp",
|
||||||
|
"salt", "salt", "condiments"));
|
||||||
|
testList.add(new ShopIngredient(0.5, "tsp",
|
||||||
|
"pepper", "pepper", "condiments"));
|
||||||
|
testList.add(new ShopIngredient(2.0d, "cup",
|
||||||
|
"milk", "milk", "dairy"));
|
||||||
|
testList.add(new ShopIngredient(0.75d, "cup",
|
||||||
|
"sugar", "sugar", "baking"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
ShoppingListBean.optimizeIngredients(testList);
|
||||||
|
assertEquals(4, testList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user