Have downloadable shopping list

This commit is contained in:
Tim Holloway 2022-01-15 18:06:45 -05:00
parent 75a8487cfb
commit 18410682de
5 changed files with 166 additions and 30 deletions

View File

@ -25,7 +25,7 @@ public class ShopIngredient implements Comparable<Object> {
* @param ingkey * @param ingkey
* @param shopCat * @param shopCat
*/ */
public ShopIngredient( double amount, String unit, public ShopIngredient( Double amount, String unit,
String item, String ingkey, String item, String ingkey,
String shopCat) { String shopCat) {
this.amount = amount; this.amount = amount;
@ -68,14 +68,14 @@ public class ShopIngredient implements Comparable<Object> {
/** /**
* @return the amount * @return the amount
*/ */
public double getAmount() { public Double getAmount() {
return amount; return amount;
} }
/** /**
* @param amount the amount to set * @param amount the amount to set
*/ */
public void setAmount(double amount) { public void setAmount(Double amount) {
this.amount = amount; this.amount = amount;
} }
@ -83,8 +83,12 @@ public class ShopIngredient implements Comparable<Object> {
* @return the displayAmount * @return the displayAmount
*/ */
public String getDisplayAmount() { public String getDisplayAmount() {
Double amt = this.getAmount();
if ( amt == null) {
return "";
}
return IngredientDigester.displayAmount( return IngredientDigester.displayAmount(
IngredientAmountFormat.IA_TEXT, this.getAmount(), IngredientAmountFormat.IA_TEXT, amt,
null); null);
} }
@ -112,7 +116,7 @@ public class ShopIngredient implements Comparable<Object> {
} }
private String ingkey; private String ingkey;
private double amount; private Double amount;
private String displayAmount; private String displayAmount;
private String unit; private String unit;

View File

@ -1,17 +1,22 @@
package com.mousetech.gourmetj; package com.mousetech.gourmetj;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.view.ViewScoped; import javax.faces.view.ViewScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.primefaces.model.ByteArrayContent;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,11 +24,57 @@ import com.mousetech.gourmetj.persistence.dao.ShopcatRepository;
import com.mousetech.gourmetj.persistence.model.Ingredient; import com.mousetech.gourmetj.persistence.model.Ingredient;
import com.mousetech.gourmetj.persistence.model.Recipe; import com.mousetech.gourmetj.persistence.model.Recipe;
import com.mousetech.gourmetj.persistence.model.Shopcat; import com.mousetech.gourmetj.persistence.model.Shopcat;
import com.mousetech.gourmetj.utils.YamlShoppingList;
@Named @Named
@ViewScoped @ViewScoped
public class ShoppingListBean implements Serializable { public class ShoppingListBean implements Serializable {
public class RecipeReference {
private int count;
private Recipe recipe;
/**
* Constructor Constructor.
*
* @param r Recipe to reference (from Shopping List)
*/
public RecipeReference(Recipe r) {
count = 1;
recipe = r;
}
/**
* @return the count
*/
public int getCount() {
return count;
}
/**
* @param count the count to set
*/
public void setCount(int count) {
this.count = count;
}
/**
* @return the recipe
*/
public Recipe getRecipe() {
return recipe;
}
/**
* @param recipe the recipe to set
*/
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
}
/** /**
* Serial version for session save/restore * Serial version for session save/restore
*/ */
@ -41,7 +92,7 @@ public class ShoppingListBean implements Serializable {
private List<ShopIngredient> siList; private List<ShopIngredient> siList;
// private List<ShopIngredient> ingredientList; private List<RecipeReference> recipeList;
@PostConstruct @PostConstruct
public void init() { public void init() {
@ -50,8 +101,19 @@ public class ShoppingListBean implements Serializable {
buildMaps(); buildMaps();
} }
public List<Recipe> getRecipeList() { public List<RecipeReference> getRecipeList() {
return this.userSession.getShoppingList(); if (this.recipeList == null) {
this.recipeList = loadRecipeList();
}
return this.recipeList;
}
private List<RecipeReference> loadRecipeList() {
List<RecipeReference> list =
userSession.getShoppingList().stream()
.map(r -> new RecipeReference(r))
.collect(Collectors.toList());
return list;
} }
public List<ShopIngredient> getIngredientList() { public List<ShopIngredient> getIngredientList() {
@ -59,7 +121,8 @@ public class ShoppingListBean implements Serializable {
} }
private void buildMaps() { private void buildMaps() {
for (Recipe r : this.getRecipeList()) { this.siList = new ArrayList<ShopIngredient>(30);
for (RecipeReference r : this.getRecipeList()) {
buildMapsFor(r); buildMapsFor(r);
} }
// Now consolidate amounts and sort by // Now consolidate amounts and sort by
@ -77,8 +140,13 @@ public class ShoppingListBean implements Serializable {
* *
* @see #buildMaps() * @see #buildMaps()
*/ */
private void buildMapsFor(Recipe r) { private void buildMapsFor(RecipeReference r) {
for (Ingredient ing : r.getIngredientHash()) { final int multiplier = r.getCount();
if (multiplier == 0) {
return;
}
for (Ingredient ing : r.getRecipe()
.getIngredientHash()) {
String ingkey = ing.getIngkey(); String ingkey = ing.getIngkey();
if (StringUtils.isBlank(ingkey)) { if (StringUtils.isBlank(ingkey)) {
continue; continue;
@ -86,10 +154,21 @@ public class ShoppingListBean implements Serializable {
String shopCatName = ing.getShopCat() != null String shopCatName = ing.getShopCat() != null
? ing.getShopCat().getShopcategory() ? ing.getShopCat().getShopcategory()
: null; : null;
ShopIngredient sing = new ShopIngredient( ShopIngredient sing;
ing.getAmount(), ing.getUnit(), try {
ing.getItem(), ing.getIngkey(), shopCatName); Double amt = ing.getAmount();
if (multiplier > 1 && (amt != null)) {
amt *= multiplier;
}
sing = new ShopIngredient(amt, ing.getUnit(),
ing.getItem(), ing.getIngkey(),
shopCatName);
siList.add(sing); siList.add(sing);
} catch (Exception e) {
log.error("Unable to create ShopIngredient for "
+ r.getRecipe() + " Ingredient " + ing);
e.printStackTrace();
}
} }
} }
@ -150,6 +229,12 @@ public class ShoppingListBean implements Serializable {
} }
} }
public StreamedContent getDlIngredientList() {
return YamlShoppingList
.createDownload(getIngredientList());
}
// =============================================
private List<String> shopcatList; private List<String> shopcatList;
public List<String> getShopcatList() { public List<String> getShopcatList() {
@ -211,6 +296,15 @@ public class ShoppingListBean implements Serializable {
this.shopcatList = null; this.shopcatList = null;
} }
/**
* Primefaces AJAX listener for changes to amount values of
* recipes the recipe list. Forces re-computation of
* ingredient requirements.
*/
public void pfAmountChange() {
buildMaps();
}
// === // ===
private String selectedShopcat; private String selectedShopcat;
@ -295,8 +389,8 @@ public class ShoppingListBean implements Serializable {
this.shopcatRepository this.shopcatRepository
.deleteShopcatFor(this.getSelectedIngkey()); .deleteShopcatFor(this.getSelectedIngkey());
} else { } else {
this.shopcatRepository this.shopcatRepository.updateShopcatFor(newCat,
.updateShopcatFor(newCat, this.getSelectedIngkey()); this.getSelectedIngkey());
} }
} }

View File

@ -23,7 +23,6 @@ textarea {
} }
#footer { #footer {
position: absolute;
bottom: 90px; bottom: 90px;
width: 100%; width: 100%;
} }

View File

@ -33,7 +33,9 @@
<f:selectItems <f:selectItems
value="#{userSession.searchTypeList}" value="#{userSession.searchTypeList}"
/> />
<p:ajax listener="#{adminMainBean.resetSuggestions}"/> <p:ajax
listener="#{adminMainBean.resetSuggestions}"
/>
</p:selectOneMenu> </p:selectOneMenu>
<p:commandButton id="ctlClear" value="Clear" <p:commandButton id="ctlClear" value="Clear"
icon="ui-icon-close" icon="ui-icon-close"
@ -46,6 +48,13 @@
<p:commandButton value="More..." <p:commandButton value="More..."
action="#{adminMainBean.doMore}" action="#{adminMainBean.doMore}"
/> />
<h:outputText id="slistSSize"
style="margin-left: 2em"
value="#{userSession.shoppingList.size()}"
/>
<h:outputLabel for="slistSize"
value=" Recipes in Shopping List"
/>
</div> </div>
</h:form> </h:form>
<h:form id="form2"> <h:form id="form2">

View File

@ -23,6 +23,15 @@
border-width: 0; border-width: 0;
border-style: none; border-style: none;
} }
.noRecipe {
text-decoration: line-through;
color: gray;
}
.plusRecipe {
}
</style> </style>
<h:messages /> <h:messages />
<p:tabView id="tabGroupClient" orientation="left" <p:tabView id="tabGroupClient" orientation="left"
@ -38,11 +47,31 @@
<f:facet name="header"> <f:facet name="header">
<h:outputText value="Recipes" /> <h:outputText value="Recipes" />
</f:facet> </f:facet>
<p:column style="width: 4em">
<p:spinner required="true" min="0"
value="#{item.count}" size="1"
>
<p:ajax
listener="#{shoppingListBean.pfAmountChange}"
update="@form:tblShopIngredients rname"
/>
</p:spinner>
</p:column>
<p:column> <p:column>
<h:outputText value="#{item.title}" /> <h:outputText id="rname"
styleClass="#{(item.count eq 0) ? 'noRecipe' :'plusRecipe' }"
value="#{item.recipe.title}"
/>
</p:column> </p:column>
</p:dataTable> </p:dataTable>
<!-- ====== Ingredients ============================ --> <!-- ====== Ingredients To Buy ======================= -->
<p:column id="dlIng">
<p:commandButton value="Download List" ajax="false">
<p:fileDownload
value="#{shoppingListBean.dlIngredientList}"
/>
</p:commandButton>
</p:column>
<p:column id="ingredientsc" <p:column id="ingredientsc"
style="width: 25%; vertical-align: top;" style="width: 25%; vertical-align: top;"
> >
@ -64,11 +93,6 @@
/> />
</p:column> </p:column>
</p:headerRow> </p:headerRow>
<p:column label="ingKey">
<h:outputText
value="#{item.ingkey}"
/>
</p:column>
<p:column label="Amt" <p:column label="Amt"
style="width: 3em; text-align: right" style="width: 3em; text-align: right"
> >
@ -95,11 +119,17 @@
</h:form> </h:form>
</p:tab> </p:tab>
<!-- --> <!-- -->
<p:tab id="ingshopcatEditTab" title="Edit"> <p:tab id="ingshopcatEditTab"
title="Edit Shopping Categories"
>
<ui:include <ui:include
src="/WEB-INF/layout/misctabs/ingshopkey.xhtml" src="/WEB-INF/layout/misctabs/ingshopkey.xhtml"
/> />
</p:tab> </p:tab>
<!-- -->
<p:tab id="tabImportExport" title="Import/Export">
<h:outputText value="For future implementation" />
</p:tab>
</p:tabView> </p:tabView>
<h:form id="frmHome"> <h:form id="frmHome">
<p:commandButton id="doHome" value="Home" <p:commandButton id="doHome" value="Home"