mirror of
https://github.com/shouptech/humulus.git
synced 2026-02-03 23:19:44 +00:00
Add yeast
This commit is contained in:
parent
c74ae966a3
commit
98404e38ef
3 changed files with 154 additions and 13 deletions
|
|
@ -20,7 +20,7 @@ from flask import Blueprint, flash, redirect, render_template, url_for
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (Form, StringField, DecimalField, TextAreaField, FieldList,
|
from wtforms import (Form, StringField, DecimalField, TextAreaField, FieldList,
|
||||||
FormField, SelectField)
|
FormField, SelectField)
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired, Optional
|
||||||
|
|
||||||
from humulus.couch import get_doc_or_404, put_doc
|
from humulus.couch import get_doc_or_404, put_doc
|
||||||
|
|
||||||
|
|
@ -85,6 +85,62 @@ class HopForm(Form):
|
||||||
'amount': str(self.amount.data)
|
'amount': str(self.amount.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class YeastForm(Form):
|
||||||
|
"""Form for yeast.
|
||||||
|
|
||||||
|
CSRF is disabled for this subform (using `Form as parent class) because it
|
||||||
|
is never used by itself.
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
type = SelectField('Type', default='',
|
||||||
|
choices=[(c, c) for c in ['', 'Liquid', 'Dry']],
|
||||||
|
validators=[Optional()])
|
||||||
|
lab = StringField('Lab')
|
||||||
|
code = StringField('Lab Code')
|
||||||
|
flocculation = SelectField('Flocculation', default='',
|
||||||
|
choices=[(c, c) for c in ['', 'Low',
|
||||||
|
'Medium', 'High']],
|
||||||
|
validators=[Optional()])
|
||||||
|
low_attenuation = DecimalField('Low Attenuation',
|
||||||
|
validators=[DataRequired()])
|
||||||
|
high_attenuation = DecimalField('High Attenuation',
|
||||||
|
validators=[DataRequired()])
|
||||||
|
min_temperature = DecimalField('Min Temp (°F)',
|
||||||
|
validators=[Optional()])
|
||||||
|
max_temperature = DecimalField('Max Temp (°F)',
|
||||||
|
validators=[Optional()])
|
||||||
|
abv_tolerance = DecimalField('ABV % tolerance',
|
||||||
|
validators=[Optional()])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def doc(self):
|
||||||
|
"""Returns a dictionary that can be deserialized into JSON.
|
||||||
|
|
||||||
|
Used for putting into CouchDB.
|
||||||
|
"""
|
||||||
|
yeast = {
|
||||||
|
'name': self.name.data,
|
||||||
|
'low_attenuation': str(self.low_attenuation.data),
|
||||||
|
'high_attenuation': str(self.high_attenuation.data)
|
||||||
|
}
|
||||||
|
if self.type.data:
|
||||||
|
yeast['type'] = self.type.data
|
||||||
|
if self.lab.data:
|
||||||
|
yeast['lab'] = self.lab.data
|
||||||
|
if self.code.data:
|
||||||
|
yeast['code'] = self.code.data
|
||||||
|
if self.flocculation.data:
|
||||||
|
yeast['flocculation'] = self.flocculation.data
|
||||||
|
if self.min_temperature.data:
|
||||||
|
yeast['min_temperature'] = str(self.min_temperature.data)
|
||||||
|
if self.max_temperature.data:
|
||||||
|
yeast['max_temperature'] = str(self.max_temperature.data)
|
||||||
|
if self.abv_tolerance.data:
|
||||||
|
yeast['abv_tolerance'] = str(self.abv_tolerance.data)
|
||||||
|
return yeast
|
||||||
|
|
||||||
|
|
||||||
class RecipeForm(FlaskForm):
|
class RecipeForm(FlaskForm):
|
||||||
"""Form for recipes."""
|
"""Form for recipes."""
|
||||||
name = StringField('Name', validators=[DataRequired()])
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
|
@ -102,6 +158,7 @@ class RecipeForm(FlaskForm):
|
||||||
min_entries=0,
|
min_entries=0,
|
||||||
max_entries=20
|
max_entries=20
|
||||||
)
|
)
|
||||||
|
yeast = FormField(YeastForm)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def doc(self):
|
def doc(self):
|
||||||
|
|
@ -109,15 +166,21 @@ class RecipeForm(FlaskForm):
|
||||||
|
|
||||||
Used for putting into CouchDB.
|
Used for putting into CouchDB.
|
||||||
"""
|
"""
|
||||||
return {
|
recipe = {
|
||||||
'name': self.name.data,
|
'name': self.name.data,
|
||||||
'efficiency': str(self.efficiency.data),
|
'efficiency': str(self.efficiency.data),
|
||||||
'volume': str(self.volume.data),
|
'volume': str(self.volume.data),
|
||||||
'notes': self.notes.data,
|
'notes': self.notes.data
|
||||||
'fermentables': [f.doc for f in self.fermentables],
|
|
||||||
'hops': [h.doc for h in self.hops],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(self.fermentables) > 0:
|
||||||
|
recipe['fermentables'] = [f.doc for f in self.fermentables]
|
||||||
|
if len(self.hops) > 0:
|
||||||
|
recipe['hops'] = [h.doc for h in self.hops]
|
||||||
|
if self.yeast.doc['name']:
|
||||||
|
recipe['yeast'] = self.yeast.doc
|
||||||
|
return recipe
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/create', methods=('GET', 'POST'))
|
@bp.route('/create', methods=('GET', 'POST'))
|
||||||
def create():
|
def create():
|
||||||
|
|
@ -127,7 +190,6 @@ def create():
|
||||||
response = put_doc(form.doc)
|
response = put_doc(form.doc)
|
||||||
flash('Created recipe: {}'.format(form.name.data), 'success')
|
flash('Created recipe: {}'.format(form.name.data), 'success')
|
||||||
return redirect(url_for('recipes.info', id=response['_id']))
|
return redirect(url_for('recipes.info', id=response['_id']))
|
||||||
|
|
||||||
return render_template('recipes/create.html', form=form)
|
return render_template('recipes/create.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,28 @@
|
||||||
<button type="button" id="add-hop" class="btn btn-secondary btn-sm">Add a hop</button>
|
<button type="button" id="add-hop" class="btn btn-secondary btn-sm">Add a hop</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#-
|
||||||
|
Recipe Yeast
|
||||||
|
-#}
|
||||||
|
<div class="row"><div class="col"><h3>Yeast</h3></div></div>
|
||||||
|
<div id="yeast">
|
||||||
|
<div class="border pl-2 pr-2 pt-1 pb-1 yeast-form">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">{{ render_field_with_errors(form.yeast.form.name, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm-2">{{ render_field_with_errors(form.yeast.form.type, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm-2">{{ render_field_with_errors(form.yeast.form.lab, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm-2">{{ render_field_with_errors(form.yeast.form.code, 'form-control-sm') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.low_attenuation, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.high_attenuation, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.flocculation, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.min_temperature, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.max_temperature, 'form-control-sm') }}</div>
|
||||||
|
<div class="col-sm">{{ render_field_with_errors(form.yeast.form.abv_tolerance, 'form-control-sm') }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{#-
|
{#-
|
||||||
Recipe Notes
|
Recipe Notes
|
||||||
-#}
|
-#}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from humulus.couch import get_db
|
from humulus.couch import get_db
|
||||||
from humulus.recipes import FermentableForm, HopForm, RecipeForm
|
from humulus.recipes import FermentableForm, HopForm, RecipeForm, YeastForm
|
||||||
|
|
||||||
|
|
||||||
def test_create(client, app):
|
def test_create(client, app):
|
||||||
|
|
@ -29,7 +29,10 @@ def test_create(client, app):
|
||||||
'efficiency': '65',
|
'efficiency': '65',
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
'notes': 'Test',
|
'notes': 'Test',
|
||||||
'volume': '5.5'
|
'volume': '5.5',
|
||||||
|
'yeast-name': 'Test',
|
||||||
|
'yeast-low_attenuation': '60',
|
||||||
|
'yeast-high_attenuation': '75',
|
||||||
}
|
}
|
||||||
response = client.post('/recipes/create', data=data)
|
response = client.post('/recipes/create', data=data)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
|
|
@ -55,6 +58,41 @@ def test_info(client):
|
||||||
assert b'Awesome Lager' in response.data
|
assert b'Awesome Lager' in response.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_yeast_form_doc(app):
|
||||||
|
"""Evaluates conditionals in generation of doc from a yeast form."""
|
||||||
|
yeast = YeastForm()
|
||||||
|
yeast.name.data = 'Test'
|
||||||
|
yeast.low_attenuation.data = Decimal('60')
|
||||||
|
yeast.high_attenuation.data = Decimal('75')
|
||||||
|
|
||||||
|
assert yeast.doc == {
|
||||||
|
'name': 'Test',
|
||||||
|
'low_attenuation': '60',
|
||||||
|
'high_attenuation': '75'
|
||||||
|
}
|
||||||
|
|
||||||
|
yeast.type.data = 'Dry'
|
||||||
|
yeast.code.data = 'INIS-001'
|
||||||
|
yeast.lab.data = 'Inland Island'
|
||||||
|
yeast.flocculation.data = 'Low'
|
||||||
|
yeast.min_temperature.data = Decimal('40')
|
||||||
|
yeast.max_temperature.data = Decimal('50')
|
||||||
|
yeast.abv_tolerance.data = Decimal('15')
|
||||||
|
|
||||||
|
assert yeast.doc == {
|
||||||
|
'name': 'Test',
|
||||||
|
'low_attenuation': '60',
|
||||||
|
'high_attenuation': '75',
|
||||||
|
'flocculation': 'Low',
|
||||||
|
'type': 'Dry',
|
||||||
|
'code': 'INIS-001',
|
||||||
|
'lab': 'Inland Island',
|
||||||
|
'min_temperature': '40',
|
||||||
|
'max_temperature': '50',
|
||||||
|
'abv_tolerance': '15'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_recipe_form_doc(app):
|
def test_recipe_form_doc(app):
|
||||||
"""Test if a recipeform can be turned into a document.
|
"""Test if a recipeform can be turned into a document.
|
||||||
|
|
||||||
|
|
@ -64,6 +102,18 @@ def test_recipe_form_doc(app):
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
recipe = RecipeForm()
|
recipe = RecipeForm()
|
||||||
|
|
||||||
|
recipe.name.data = 'Test'
|
||||||
|
recipe.efficiency.data = Decimal('65')
|
||||||
|
recipe.volume.data = Decimal('5.5')
|
||||||
|
recipe.notes.data = 'This is a test'
|
||||||
|
|
||||||
|
assert recipe.doc == {
|
||||||
|
'name': 'Test',
|
||||||
|
'efficiency': '65',
|
||||||
|
'volume': '5.5',
|
||||||
|
'notes': 'This is a test',
|
||||||
|
}
|
||||||
|
|
||||||
ferm = FermentableForm()
|
ferm = FermentableForm()
|
||||||
ferm.name.data = 'Test'
|
ferm.name.data = 'Test'
|
||||||
ferm.type.data = 'Grain'
|
ferm.type.data = 'Grain'
|
||||||
|
|
@ -78,12 +128,14 @@ def test_recipe_form_doc(app):
|
||||||
hop.duration.data = Decimal('60')
|
hop.duration.data = Decimal('60')
|
||||||
hop.amount.data = Decimal('0.5')
|
hop.amount.data = Decimal('0.5')
|
||||||
|
|
||||||
recipe.name.data = 'Test'
|
yeast = YeastForm()
|
||||||
recipe.efficiency.data = Decimal('65')
|
yeast.name.data = 'Test'
|
||||||
recipe.volume.data = Decimal('5.5')
|
yeast.low_attenuation.data = '70'
|
||||||
recipe.notes.data = 'This is a test'
|
yeast.high_attenuation.data = '75'
|
||||||
|
|
||||||
recipe.fermentables = [ferm]
|
recipe.fermentables = [ferm]
|
||||||
recipe.hops = [hop]
|
recipe.hops = [hop]
|
||||||
|
recipe.yeast = yeast
|
||||||
|
|
||||||
assert recipe.doc == {
|
assert recipe.doc == {
|
||||||
'name': 'Test',
|
'name': 'Test',
|
||||||
|
|
@ -103,5 +155,10 @@ def test_recipe_form_doc(app):
|
||||||
'alpha': '12.5',
|
'alpha': '12.5',
|
||||||
'duration': '60',
|
'duration': '60',
|
||||||
'amount': '0.5'
|
'amount': '0.5'
|
||||||
}]
|
}],
|
||||||
|
'yeast': {
|
||||||
|
'name': 'Test',
|
||||||
|
'low_attenuation': '70',
|
||||||
|
'high_attenuation': '75'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue