Compare commits
10 Commits
800bfea227
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79b24ae332 | ||
|
|
a7b52593d1 | ||
|
|
76b806a803 | ||
|
|
0338cd83ed | ||
|
|
a446be7e8b | ||
|
|
c474aaae3f | ||
|
|
70a86fc06f | ||
|
|
0f106822c7 | ||
|
|
18aa65c053 | ||
|
|
6840cc506d |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/venv
|
||||||
|
/__pycache__/
|
||||||
|
/.idea
|
||||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
10
.idea/PortfolioProject.iml
generated
10
.idea/PortfolioProject.iml
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
13
.idea/inspectionProfiles/Project_Default.xml
generated
13
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,13 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
||||||
<option name="ignoredIdentifiers">
|
|
||||||
<list>
|
|
||||||
<option value="object.is_tombstone" />
|
|
||||||
<option value="object.key" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/PortfolioProject.iml" filepath="$PROJECT_DIR$/.idea/PortfolioProject.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
14
README.md
14
README.md
@@ -1,3 +1,13 @@
|
|||||||
Here's the README for my portfolio project for CS361.
|
# BreadApp
|
||||||
|
|
||||||
Not much to see yet.
|
Built for real world use in storing and modify recipes for sourdough bread.
|
||||||
|
|
||||||
|
Built using Flask and JSON for data persistence.
|
||||||
|
|
||||||
|
To run, create a virtual environment with `python3 -m venv venv` or however you see fit, followed by:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>`source venv/bin/activate` (or similar depending on your venv usage</li>
|
||||||
|
<li>`pip install -r requirements.txt`</li>
|
||||||
|
<li>`python main.py`</li>
|
||||||
|
</ul>
|
||||||
Binary file not shown.
Binary file not shown.
3
mail.txt
Normal file
3
mail.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
$to == jenseluc@oregonstate.edu
|
||||||
|
$subject == White Bread SD Conversion
|
||||||
|
$message == White Bread SD Conversion; Num loaves: 2; AP Flour: 790 grams; WW Flour: 90 grams; Rye Flour: 95 grams; Water: 625 grams; Salt: 25 grams; Yeast: 0 grams; Starter: 50 grams;
|
||||||
87
mailer.py
Normal file
87
mailer.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
from mailjet_rest import Client
|
||||||
|
|
||||||
|
api_key = '9aa5c72ce9c248d0570aa1e6cabdc9ab'
|
||||||
|
api_secret = '9bed4b34d09b8e19017768cc8264479e'
|
||||||
|
mailjet = Client(auth=(api_key, api_secret), version='v3.1')
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Sending ======>")
|
||||||
|
# read maile.txt
|
||||||
|
with open('mail.txt', 'rt') as infile:
|
||||||
|
text = infile.read()
|
||||||
|
|
||||||
|
# when text start with '$' it is the key and the rest is the value
|
||||||
|
# split the text into a list of lines
|
||||||
|
lines = text.split('\n')
|
||||||
|
# loop through the lines
|
||||||
|
theMail = {}
|
||||||
|
to = ""
|
||||||
|
subject = ""
|
||||||
|
body = ""
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for line in lines:
|
||||||
|
# if the line starts with '$'
|
||||||
|
|
||||||
|
if line.startswith('$'):
|
||||||
|
# split the line into key and value
|
||||||
|
key, value = line.split(' ', 1)
|
||||||
|
|
||||||
|
# if the key is 'email'
|
||||||
|
if key == '$to':
|
||||||
|
to = value.replace("==", "").strip()
|
||||||
|
elif key == '$subject':
|
||||||
|
|
||||||
|
subject = value.replace("==", "").strip()
|
||||||
|
elif key == '$message':
|
||||||
|
body = value.replace("==", "").strip()
|
||||||
|
elif line.startswith('#'):
|
||||||
|
#reset variables
|
||||||
|
to = ""
|
||||||
|
subject = ""
|
||||||
|
body = ""
|
||||||
|
|
||||||
|
if to != "" and subject != "" and body != "":
|
||||||
|
|
||||||
|
#add them to the dictionary
|
||||||
|
theMail[i] = {'to': to, 'subject': subject, 'body': body}
|
||||||
|
i += 1
|
||||||
|
# reset the variables
|
||||||
|
to = ""
|
||||||
|
subject = ""
|
||||||
|
body = ""
|
||||||
|
# if #end is found, reset the variables
|
||||||
|
# loop through the dictionary
|
||||||
|
for key, value in theMail.items():
|
||||||
|
# send the email
|
||||||
|
send_email(value['to'], value['subject'], value['body'])
|
||||||
|
# print(value['to'])
|
||||||
|
# print(value['subject'])
|
||||||
|
# print(value['body'])
|
||||||
|
# print(theMail)
|
||||||
|
|
||||||
|
def send_email(email, subject, body):
|
||||||
|
data = {
|
||||||
|
'Messages': [
|
||||||
|
{
|
||||||
|
"From": {
|
||||||
|
"Email": "khansom@oregonstate.edu",
|
||||||
|
"Name": "Soman Khan"
|
||||||
|
},
|
||||||
|
"To": [
|
||||||
|
{
|
||||||
|
"Email": email,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Subject": subject,
|
||||||
|
"TextPart": "Greetings!",
|
||||||
|
"HTMLPart": body,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
result = mailjet.send.create(data=data)
|
||||||
|
print(result.status_code)
|
||||||
|
print(result.json())
|
||||||
|
# call main
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
110
main.py
110
main.py
@@ -1,20 +1,24 @@
|
|||||||
"""
|
"""
|
||||||
Portfolio Project for CS361 Spring 2022
|
Portfolio Project for CS361 Spring 2022
|
||||||
Written by Lucas Jensen
|
Written by Lucas Jensen
|
||||||
Last updated 3/29/22 for Assignment 1
|
Last updated 10-12-2023 for deployment
|
||||||
"""
|
"""
|
||||||
from flask import Flask, redirect, render_template, request
|
from flask import Flask, redirect, render_template, request, send_file
|
||||||
from recipe import Recipe, RecipeBook
|
from recipe import Recipe, RecipeBook
|
||||||
|
from time import sleep
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
book = RecipeBook()
|
book = RecipeBook()
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def home():
|
def home():
|
||||||
return render_template("index.html")
|
return render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/recipes', methods=['GET'])
|
@app.route("/recipes", methods=["GET"])
|
||||||
def recipes_page():
|
def recipes_page():
|
||||||
recipes = book.get_recipes()
|
recipes = book.get_recipes()
|
||||||
# recipes is a dictionary of recipe objects
|
# recipes is a dictionary of recipe objects
|
||||||
@@ -23,31 +27,107 @@ def recipes_page():
|
|||||||
return render_template("recipes.html", content=recipes)
|
return render_template("recipes.html", content=recipes)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/recipes/<_id>', methods=['GET', 'POST'])
|
@app.route("/recipes/<_id>/email", methods=["GET", "POST"])
|
||||||
|
def email_page(_id):
|
||||||
|
recipe = book.find_by_id(str(_id))
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
addr = request.form.get("email")
|
||||||
|
subject = recipe.get_name()
|
||||||
|
message = f"{subject}; " f"Num loaves: {recipe.get_num_loaves()};"
|
||||||
|
ingredients = recipe.get_ingredients()
|
||||||
|
for item in ingredients:
|
||||||
|
message += f" {item}: {ingredients[item]} grams;"
|
||||||
|
|
||||||
|
body = f"$to == {addr}\n" f"$subject == {subject}\n" f"$message == {message}"
|
||||||
|
|
||||||
|
with open("mail.txt", "wt") as txt_file:
|
||||||
|
txt_file.write(body)
|
||||||
|
|
||||||
|
subprocess.call(["python", "mailer.py"])
|
||||||
|
|
||||||
|
return redirect("/recipes")
|
||||||
|
|
||||||
|
return render_template("email.html", content=recipe)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/recipes/<_id>", methods=["GET", "POST"])
|
||||||
def recipe_page(_id):
|
def recipe_page(_id):
|
||||||
recipe = book.find_by_id(_id)
|
recipe = book.find_by_id(_id)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
scale = request.form.get('scale')
|
# user wants to scale their recipe
|
||||||
new = recipe.scale(scale)
|
if "scale" in request.form:
|
||||||
new_id = book.add_recipe(new)
|
scale = request.form.get("scale")
|
||||||
return redirect(f'/recipes/{new_id}')
|
new = recipe.scale(scale)
|
||||||
|
new_id = book.add_recipe(new)
|
||||||
|
return redirect(f"/recipes/{new_id}")
|
||||||
|
elif "photo" in request.files:
|
||||||
|
# user wants to add a photo
|
||||||
|
f = request.files["photo"]
|
||||||
|
f.save(os.path.join("static", f"image_{_id}.jpg"))
|
||||||
|
# return "Success"
|
||||||
|
elif "convert" in request.form:
|
||||||
|
# user wants to convert to sourdough
|
||||||
|
new_recipe = recipe.convert()
|
||||||
|
book.add_recipe(new_recipe)
|
||||||
|
return redirect("/recipes")
|
||||||
|
else:
|
||||||
|
# user wants to download a pdf
|
||||||
|
path = write_txt(_id)
|
||||||
|
sleep(0.5)
|
||||||
|
return send_file(path, as_attachment=True)
|
||||||
|
|
||||||
return render_template("recipe.html", content=recipe, _id=_id)
|
# find all associated images
|
||||||
|
recipe_photos = []
|
||||||
|
all_photos = os.listdir("static")
|
||||||
|
for photo in all_photos:
|
||||||
|
if "jpg" in photo:
|
||||||
|
if get_num(photo) == int(_id):
|
||||||
|
recipe_photos.append(photo)
|
||||||
|
|
||||||
|
return render_template("recipe.html", content=recipe, _id=_id, photos=recipe_photos)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/recipes/<_id>/delete')
|
def get_num(path: str) -> int:
|
||||||
|
"""
|
||||||
|
:param path: must be formatted: "image_XXX.jpg" where XXX is the id of the
|
||||||
|
recipe with any number of digits
|
||||||
|
:return: integer of the found id number
|
||||||
|
"""
|
||||||
|
num = ""
|
||||||
|
for i in range(6, len(path)):
|
||||||
|
if path[i] == ".":
|
||||||
|
break
|
||||||
|
num += path[i]
|
||||||
|
|
||||||
|
return int(num)
|
||||||
|
|
||||||
|
|
||||||
|
def write_txt(_id):
|
||||||
|
"""
|
||||||
|
writes to the txt file to have the microservice make a selected pdf
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
print(_id)
|
||||||
|
with open("recipe.txt", "w") as txt_file:
|
||||||
|
txt_file.write(_id)
|
||||||
|
|
||||||
|
return f"recipe{_id}.pdf"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/recipes/<_id>/delete")
|
||||||
def delete_recipe(_id):
|
def delete_recipe(_id):
|
||||||
book.find_and_delete(_id)
|
book.find_and_delete(_id)
|
||||||
return redirect("/recipes")
|
return redirect("/recipes")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/add', methods=['GET', 'POST'])
|
@app.route("/add", methods=["GET", "POST"])
|
||||||
def add_recipe():
|
def add_recipe():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
new_recipe = Recipe(request.form.get('name'), request.form.get('yield'))
|
new_recipe = Recipe(request.form.get("name"), request.form.get("yield"))
|
||||||
for item in request.form:
|
for item in request.form:
|
||||||
if item not in ['name', 'yield']:
|
if item not in ["name", "yield"]:
|
||||||
new_recipe.add_ingredient(item, int(request.form.get(item)))
|
new_recipe.add_ingredient(item, int(request.form.get(item)))
|
||||||
|
|
||||||
book.add_recipe(new_recipe)
|
book.add_recipe(new_recipe)
|
||||||
@@ -57,4 +137,4 @@ def add_recipe():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(debug=False)
|
app.run(debug=False, port=5001)
|
||||||
|
|||||||
117
pdf-generator.py
Normal file
117
pdf-generator.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
Reads a key from a txt file. Searches a json file with the key.
|
||||||
|
Exports the key values in a pdf form. Writes the PDF file name
|
||||||
|
to the txt file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from fpdf import FPDF
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
TXTFILE = 'recipe.txt' # update to txt file name for read
|
||||||
|
JSONFILE = 'recipes.json' # update to json file name for read
|
||||||
|
CATEGORY = 'recipe' # update category to preceed number in PDF file name
|
||||||
|
|
||||||
|
|
||||||
|
def create_pdf() -> object:
|
||||||
|
"""
|
||||||
|
Create pdf object from FPDF class
|
||||||
|
:return: PDF object
|
||||||
|
"""
|
||||||
|
pdf = FPDF()
|
||||||
|
pdf.add_page()
|
||||||
|
pdf.set_font("Arial", size=15)
|
||||||
|
return pdf
|
||||||
|
|
||||||
|
|
||||||
|
def read_txt(file_name) -> object:
|
||||||
|
"""
|
||||||
|
Open/reads txt file argument
|
||||||
|
:param file_name(str)
|
||||||
|
:return: file content
|
||||||
|
"""
|
||||||
|
with open(file_name, 'r') as txt_file:
|
||||||
|
content = txt_file.read()
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(file_name) -> object:
|
||||||
|
"""
|
||||||
|
Opens/loads json file argument
|
||||||
|
:param file_name(str)
|
||||||
|
:return: file contentquit
|
||||||
|
"""
|
||||||
|
with open(file_name, 'r') as json_file:
|
||||||
|
content = json.load(json_file)
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def add_pdf_content(txt_content, json_content, pdf) -> None:
|
||||||
|
"""
|
||||||
|
Searches json content with key from txt_content. Adds relevant data to pdf object.
|
||||||
|
:param txt_content: txt file content for json search
|
||||||
|
:param json_content: json file content to search
|
||||||
|
:param pdf: pdf object to update
|
||||||
|
:return: none
|
||||||
|
"""
|
||||||
|
for key, value in json_content.items():
|
||||||
|
if key == txt_content:
|
||||||
|
for key, value in value.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
pdf.cell(200, 10, txt=key.title() + ":", ln=1, align='C')
|
||||||
|
for key, value in value.items():
|
||||||
|
pdf.cell(200, 10, txt=key + ": " + str(value), ln=1, align='C')
|
||||||
|
else:
|
||||||
|
pdf.cell(200, 10, txt=key.title() + ": " + str(value), ln=1, align='C')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_pdf(category_name, txt_content, pdf) -> str:
|
||||||
|
"""
|
||||||
|
Generate pdf file from pdf object with relevant naming 'category + key from txt file'
|
||||||
|
:param category_name: category for pdf title
|
||||||
|
:param txt_content: txt identifier for pdf title
|
||||||
|
:param pdf: pdf object to generate pdf file
|
||||||
|
:return: pdf file name
|
||||||
|
"""
|
||||||
|
pdf.output(f"{category_name}{txt_content}.pdf")
|
||||||
|
return f"{category_name}{txt_content}.pdf"
|
||||||
|
|
||||||
|
|
||||||
|
def write_txt(file_name, pdf_file) -> None:
|
||||||
|
"""
|
||||||
|
Write generated pdf file name to txt file
|
||||||
|
:param file_name: txt file(str) to write to
|
||||||
|
:param pdf_file: pdf file name to write(str)
|
||||||
|
:return: none
|
||||||
|
"""
|
||||||
|
with open(file_name, 'w') as txt_file:
|
||||||
|
txt_file.write(pdf_file)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
initial_content = read_txt(TXTFILE) # initialize variables for while loop comparison
|
||||||
|
|
||||||
|
while True:
|
||||||
|
updated_content = read_txt(TXTFILE)
|
||||||
|
# watch for txt change
|
||||||
|
if updated_content != initial_content:
|
||||||
|
if updated_content == 'QUIT': # exit program upon cue
|
||||||
|
break
|
||||||
|
|
||||||
|
pdf = create_pdf() # create pdf object for file generation
|
||||||
|
|
||||||
|
json_content = load_json(JSONFILE) # load json content for data search
|
||||||
|
|
||||||
|
add_pdf_content(updated_content, json_content, pdf) # update pdf object with data
|
||||||
|
|
||||||
|
pdf_file_name = generate_pdf(CATEGORY, updated_content, pdf) # generate pdf file with appropriate name
|
||||||
|
|
||||||
|
write_txt(TXTFILE, pdf_file_name) # write pdf file name to txt file
|
||||||
|
|
||||||
|
initial_content = read_txt(TXTFILE) # update comparison variable
|
||||||
|
|
||||||
|
sleep(.25)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
23
recipe.py
23
recipe.py
@@ -2,12 +2,13 @@
|
|||||||
Written by Lucas Jensen
|
Written by Lucas Jensen
|
||||||
Portfolio Project for CS361
|
Portfolio Project for CS361
|
||||||
The main logic behind a recipe
|
The main logic behind a recipe
|
||||||
|
Last updated 5/12
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
JSON_FILE = "recipes/recipes.json"
|
JSON_FILE = "recipes.json"
|
||||||
|
|
||||||
|
|
||||||
class RecipeBook:
|
class RecipeBook:
|
||||||
@@ -171,6 +172,24 @@ class Recipe:
|
|||||||
|
|
||||||
return scaled_recipe
|
return scaled_recipe
|
||||||
|
|
||||||
|
def convert(self) -> object:
|
||||||
|
"""
|
||||||
|
converts a conventional recipe into a sourdough recipe
|
||||||
|
:return: the newly converted recipe object
|
||||||
|
"""
|
||||||
|
new_recipe = Recipe(f"{self._name} SD Conversion", self._num_loaves)
|
||||||
|
|
||||||
|
new_recipe._ingredients = {
|
||||||
|
'AP Flour': self._ingredients['AP Flour'] - 10,
|
||||||
|
'WW Flour': self._ingredients['WW Flour'] - 10,
|
||||||
|
'Rye Flour': self._ingredients['Rye Flour'] - 5,
|
||||||
|
'Water': self._ingredients['Water'] - 25,
|
||||||
|
'Salt': self._ingredients['Salt'],
|
||||||
|
'Yeast': 0,
|
||||||
|
'Starter': 50}
|
||||||
|
|
||||||
|
return new_recipe
|
||||||
|
|
||||||
|
|
||||||
def default_recipes():
|
def default_recipes():
|
||||||
"""Adds some sample data"""
|
"""Adds some sample data"""
|
||||||
@@ -202,3 +221,5 @@ if __name__ == "__main__":
|
|||||||
book = RecipeBook()
|
book = RecipeBook()
|
||||||
for recipe in recipes:
|
for recipe in recipes:
|
||||||
book.add_recipe(recipe)
|
book.add_recipe(recipe)
|
||||||
|
|
||||||
|
recipe = book.find_by_id(1)
|
||||||
|
|||||||
1
recipe.txt
Normal file
1
recipe.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
recipe2.pdf
|
||||||
41
recipes.json
Normal file
41
recipes.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"2": {
|
||||||
|
"quantity": "1",
|
||||||
|
"name": "Sandwich Loaf",
|
||||||
|
"ingredients": {
|
||||||
|
"AP Flour": 500,
|
||||||
|
"WW Flour": 0,
|
||||||
|
"Rye Flour": 0,
|
||||||
|
"Water": 250,
|
||||||
|
"Salt": 20,
|
||||||
|
"Yeast": 10,
|
||||||
|
"Starter": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0": {
|
||||||
|
"quantity": "1",
|
||||||
|
"name": "Country Brown Loaf",
|
||||||
|
"ingredients": {
|
||||||
|
"AP Flour": 300,
|
||||||
|
"WW Flour": 150,
|
||||||
|
"Rye Flour": 50,
|
||||||
|
"Water": 300,
|
||||||
|
"Salt": 12,
|
||||||
|
"Yeast": 10,
|
||||||
|
"Starter": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"quantity": "2",
|
||||||
|
"name": "White Bread",
|
||||||
|
"ingredients": {
|
||||||
|
"AP Flour": 800,
|
||||||
|
"WW Flour": 100,
|
||||||
|
"Rye Flour": 100,
|
||||||
|
"Water": 650,
|
||||||
|
"Salt": 25,
|
||||||
|
"Yeast": 15,
|
||||||
|
"Starter": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
python-3.10.0
|
|
||||||
18
templates/email.html
Normal file
18
templates/email.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{content.get_name()}}{% endblock %}
|
||||||
|
|
||||||
|
{% block html_head %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1>{{ content.get_name() }}</h1>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<label for="email">Email: </label>
|
||||||
|
<input name="email" id="email" type="email">
|
||||||
|
<button type="submit">Send!</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -9,20 +9,32 @@
|
|||||||
|
|
||||||
<h1>{{ content.get_name() }} Recipe</h1>
|
<h1>{{ content.get_name() }} Recipe</h1>
|
||||||
|
|
||||||
|
|
||||||
<p>Number of loaves: {{ content.get_num_loaves() }}</p>
|
<p>Number of loaves: {{ content.get_num_loaves() }}</p>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Ingredient</th>
|
<td>
|
||||||
<th>Grams</th>
|
<table>
|
||||||
</tr>
|
<tr>
|
||||||
{% for item in content.get_ingredients() %}
|
<th>Ingredient</th>
|
||||||
<tr>
|
<th>Grams</th>
|
||||||
<td>{{ item }}</td>
|
</tr>
|
||||||
<td>{{ content.get_ingredients()[item] }}</td>
|
{% for item in content.get_ingredients() %}
|
||||||
</tr>
|
<tr>
|
||||||
{% endfor %}
|
<td>{{ item }}</td>
|
||||||
|
<td>{{ content.get_ingredients()[item] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div style="padding-left: 50px">
|
||||||
|
{% for photo in photos %}
|
||||||
|
<img src="{{ url_for('static', filename=photo) }}" width="250" alt="bread photo">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Scaling</h2>
|
<h2>Scaling</h2>
|
||||||
@@ -32,7 +44,30 @@
|
|||||||
<button type="submit">Go!</button>
|
<button type="submit">Go!</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<input type="button" onclick="location.href='/recipes/{{_id}}/delete'" value=Delete />
|
{% if content._ingredients['Starter'] == 0 %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<input type="text" hidden name="convert">
|
||||||
|
<button type="submit" value="convert">Convert to SD</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form name="photo upload" id="photo upload" method="post" enctype="multipart/form-data">
|
||||||
|
<label for="photo">Upload a Photo: </label>
|
||||||
|
<input required id="photo" type="file" name="photo">
|
||||||
|
<button type="submit">Upload</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<input type="button" onclick="location.href='/recipes/{{_id}}/delete'" value=Delete />
|
||||||
|
|
||||||
|
<form name="download" id="download" method="post">
|
||||||
|
<button type="submit">Download</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<a href="/recipes/{{_id}}/email">Email me!</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user