From 40fe65cd698e13431facb115d343d5a8a20d571b Mon Sep 17 00:00:00 2001 From: Mike Shoup Date: Mon, 8 Jul 2019 14:59:50 -0600 Subject: [PATCH] Add style selection in forms --- src/humulus/recipes.py | 23 +++++++++++++++++++--- src/humulus/styles.py | 14 +++++++++++++ src/humulus/templates/recipes/_macros.html | 9 ++++++--- src/humulus/templates/recipes/info.html | 4 +++- tests/conftest.py | 9 +++++++-- tests/test_recipes.py | 22 ++++++++++++++++++++- tests/test_styles.py | 12 ++++++++++- 7 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/humulus/recipes.py b/src/humulus/recipes.py index d147532..fde93f2 100644 --- a/src/humulus/recipes.py +++ b/src/humulus/recipes.py @@ -27,7 +27,9 @@ from wtforms import (Form, StringField, DecimalField, TextAreaField, FieldList, from wtforms.validators import DataRequired, Optional from humulus.auth import login_required -from humulus.couch import get_doc_or_404, put_doc, update_doc, get_view +from humulus.couch import (get_doc, get_doc_or_404, put_doc, update_doc, + get_view) +from humulus.styles import get_styles_list bp = Blueprint('recipes', __name__, url_prefix='/recipes') @@ -169,6 +171,7 @@ class RecipeForm(FlaskForm): max_entries=20 ) yeast = FormField(YeastForm) + style = SelectField('Style', choices=[], validators=[Optional()]) @property def doc(self): @@ -182,7 +185,8 @@ class RecipeForm(FlaskForm): 'volume': str(self.volume.data), 'notes': self.notes.data, '$type': 'recipe', - 'type': self.type.data + 'type': self.type.data, + 'style': self.style.data } recipe['fermentables'] = [f.doc for f in self.fermentables] @@ -202,6 +206,7 @@ class RecipeForm(FlaskForm): self.efficiency.data = Decimal(data['efficiency']) self.volume.data = Decimal(data['volume']) self.notes.data = data['notes'] + self.style.data = data['style'] for fermentable in data['fermentables']: self.fermentables.append_entry({ @@ -282,6 +287,7 @@ def index(): @login_required def create(): form = RecipeForm() + form.style.choices = get_styles_list() if form.validate_on_submit(): response = put_doc(form.doc) @@ -308,7 +314,17 @@ def create_json(): @bp.route('/info/') def info(id): - return render_template('recipes/info.html', recipe=get_doc_or_404(id)) + recipe = get_doc_or_404(id) + + style = None + if recipe['style'] != '': + try: + style = get_doc(recipe['style']) + except KeyError: + flash('Could not find style `{}`.'.format(recipe['style']), + 'warning') + + return render_template('recipes/info.html', recipe=recipe, style=style) @bp.route('/info//json') @@ -334,6 +350,7 @@ def delete(id): def update(id): # Get the recipe from the database and validate it is the same revision form = RecipeForm() + form.style.choices = get_styles_list() recipe = get_doc_or_404(id) if form.validate_on_submit(): if recipe['_rev'] != request.args.get('rev', None): diff --git a/src/humulus/styles.py b/src/humulus/styles.py index 43e3405..a89a1e4 100644 --- a/src/humulus/styles.py +++ b/src/humulus/styles.py @@ -103,6 +103,20 @@ def import_styles(url): put_doc(doc) +def get_styles_list(): + """Returns a list containing id and names of all styles.""" + view = get_view('_design/styles', 'by-category') + styles = [['', '']] + for row in view(include_docs=False)['rows']: + print(row) + styles.append([row['id'], '{}{} {}'.format( + row['key'][0], + row['key'][1], + row['value'] + )]) + return styles + + @click.command('import-styles') @with_appcontext def import_command(): diff --git a/src/humulus/templates/recipes/_macros.html b/src/humulus/templates/recipes/_macros.html index 6ac50ac..b4f5baf 100644 --- a/src/humulus/templates/recipes/_macros.html +++ b/src/humulus/templates/recipes/_macros.html @@ -26,9 +26,12 @@ -#}
{{ render_field_with_errors(form.name) }}
-
{{ render_field_with_errors(form.type) }}
-
{{ render_field_with_errors(form.efficiency, 'ingredient-field') }}
-
{{ render_field_with_errors(form.volume, 'ingredient-field') }}
+
{{ render_field_with_errors(form.efficiency, 'ingredient-field') }}
+
{{ render_field_with_errors(form.volume, 'ingredient-field') }}
+
+
+
{{ render_field_with_errors(form.style) }}
+
{{ render_field_with_errors(form.type) }}
{#- Fermentable Ingredients diff --git a/src/humulus/templates/recipes/info.html b/src/humulus/templates/recipes/info.html index 0a8695c..c1c3e2e 100644 --- a/src/humulus/templates/recipes/info.html +++ b/src/humulus/templates/recipes/info.html @@ -20,7 +20,9 @@ {% block body %}

{{ recipe.name }}

- +{% if style %} +

{{ style.id }} {{ style.name }}

+{% endif %} {#- Recipe Details -#} diff --git a/tests/conftest.py b/tests/conftest.py index 9404281..48590a9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,7 +50,8 @@ def app(): 'notes': 'Test', 'volume': '5.5', 'fermentables': [], - 'hops': [] + 'hops': [], + 'style': '' }) put_doc({ '_id': 'partial-yeast-recipe', @@ -66,7 +67,8 @@ def app(): 'name': 'US-05', 'low_attenuation': '60', 'high_attenuation': '72', - } + }, + 'style': '' }) put_doc({ '_id': 'full-recipe', @@ -76,6 +78,7 @@ def app(): 'name': 'Awesome Beer', 'notes': 'This is a test beer that contains most possible fields.', 'volume': '2.5', + 'style': 'style_1A', 'fermentables': [ { 'name': '2row', @@ -181,6 +184,7 @@ def sample_recipes(): 'lager': { 'efficiency': '72', 'type': 'All-Grain', + 'style': '', 'fermentables': [ { 'amount': '9.5', @@ -239,6 +243,7 @@ def sample_recipes(): 'sweetstout': { 'efficiency': '72', 'type': 'All-Grain', + 'style': '', 'fermentables': [ { 'amount': '2.75', diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 43f2c2f..a0b0e2b 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -136,6 +136,7 @@ def test_create(client, app, auth): 'name': 'Test', 'notes': 'Test', 'volume': '5.5', + 'style': 'style_1A' } response = client.post('/recipes/create', data=data) assert response.status_code == 302 @@ -147,6 +148,7 @@ def test_create(client, app, auth): assert doc['notes'] == 'Test' assert doc['volume'] == '5.5' assert doc['efficiency'] == '65' + assert doc['style'] == 'style_1A' def test_update(client, app, auth): @@ -205,8 +207,12 @@ def test_update(client, app, auth): assert 'Update conflict' in flash_message -def test_info(client): +def test_info(client, monkeypatch): """Test success in retrieving a recipe document.""" + def mock_get_doc(id): + # This function always raises KeyError + raise KeyError(id) + # Validate 404 response = client.get('/recipes/info/thisdoesnotexist') assert response.status_code == 404 @@ -216,6 +222,17 @@ def test_info(client): assert response.status_code == 200 assert b'Awesome Lager' in response.data + # Validate response for recipe with style + response = client.get('/recipes/info/full-recipe') + assert response.status_code == 200 + assert b'Awesome Beer' in response.data + assert b'Test Style' in response.data + + # Validate warning is flashed when style cannot be found + monkeypatch.setattr('humulus.recipes.get_doc', mock_get_doc) + response = client.get('/recipes/info/full-recipe') + assert b'Could not find style' in response.data + def test_info_json(client): """Test success in retrieving a JSON recipe.""" @@ -279,6 +296,7 @@ def test_recipe_form_doc(app): recipe.volume.data = Decimal('5.5') recipe.notes.data = 'This is a test' recipe.type.data = 'All-Grain' + recipe.style.data = 'style_1A' assert recipe.doc == { 'name': 'Test', @@ -289,6 +307,7 @@ def test_recipe_form_doc(app): 'fermentables': [], 'hops': [], '$type': 'recipe', + 'style': 'style_1A' } ferm = FermentableForm() @@ -321,6 +340,7 @@ def test_recipe_form_doc(app): 'volume': '5.5', 'notes': 'This is a test', '$type': 'recipe', + 'style': 'style_1A', 'fermentables': [{ 'name': 'Test', 'type': 'Grain', diff --git a/tests/test_styles.py b/tests/test_styles.py index 72b321d..9ea0b6c 100644 --- a/tests/test_styles.py +++ b/tests/test_styles.py @@ -14,7 +14,7 @@ import xml.etree.ElementTree as ET -from humulus.styles import import_styles, sub_to_doc +from humulus.styles import import_styles, sub_to_doc, get_styles_list COMPLETE_STYLE = ''' Test Style @@ -182,6 +182,16 @@ def test_import_command(runner, monkeypatch): assert 'Imported BJCP styles.' in result.output +def test_get_styles_choices(app): + """Test success in getting list of styles.""" + with app.app_context(): + styles = get_styles_list() + assert styles == [ + ['', ''], + ['style_1A', '1A Test Style'] + ] + + def test_index(auth, client): """Test success in retrieving index.""" # Test not logged in