From a647bc81511f0e006bfcf42e6fabbb00ddafe75b Mon Sep 17 00:00:00 2001 From: Mike Shoup Date: Tue, 9 Jul 2019 12:32:20 -0600 Subject: [PATCH] Add page to view recipes matching a style --- src/humulus/designs/recipes.json | 3 ++ src/humulus/designs/styles.json | 2 +- src/humulus/styles.py | 17 ++++++++-- src/humulus/templates/recipes/info.html | 6 +--- src/humulus/templates/styles/index.html | 2 +- src/humulus/templates/styles/info.html | 5 +-- src/humulus/templates/styles/recipes.html | 39 +++++++++++++++++++++++ tests/conftest.py | 4 +-- tests/test_recipes.py | 10 +++--- tests/test_styles.py | 17 ++++------ 10 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 src/humulus/templates/styles/recipes.html diff --git a/src/humulus/designs/recipes.json b/src/humulus/designs/recipes.json index 28aca01..6ea08ac 100644 --- a/src/humulus/designs/recipes.json +++ b/src/humulus/designs/recipes.json @@ -13,6 +13,9 @@ }, "by-type": { "map": "function (doc) {\n if (doc.$type == \"recipe\" && doc.type && doc.name) {\n emit(doc.type, doc.name)\n }\n}" + }, + "by-style": { + "map": "function (doc) {\n if (doc.$type == \"recipe\" && doc.style && doc.name) {\n emit(doc.style, doc.name)\n }\n}" } }, "lists": {}, diff --git a/src/humulus/designs/styles.json b/src/humulus/designs/styles.json index 12185da..d216671 100644 --- a/src/humulus/designs/styles.json +++ b/src/humulus/designs/styles.json @@ -3,7 +3,7 @@ "language": "javascript", "views": { "by-category": { - "map": "function (doc) {\n if (doc.$type == \"style\") {\n category = doc.id.match(/[0-9]+|[a-zA-Z]+/g)\n category[0] = parseInt(category[0])\n emit(category, doc.name)\n }\n}" + "map": "function (doc) {\n if (doc.$type == \"style\") {\n category = doc._id.match(/[0-9]+|[a-zA-Z]+/g)\n category[0] = parseInt(category[0])\n emit(category, doc.name)\n }\n}" }, "by-name": { "map": "function (doc) {\n if (doc.$type == \"style\") {\n emit(doc.name, doc.name)\n }\n}" diff --git a/src/humulus/styles.py b/src/humulus/styles.py index a89a1e4..533defc 100644 --- a/src/humulus/styles.py +++ b/src/humulus/styles.py @@ -34,9 +34,8 @@ def sub_to_doc(sub): The returned dictionary can be placed right into CouchDB if you want. """ doc = { - '_id': 'style_{}'.format(sub.attrib['id']), + '_id': '{}'.format(sub.attrib['id']), '$type': 'style', - 'id': sub.attrib['id'], 'name': sub.find('name').text, 'aroma': sub.find('aroma').text, 'appearance': sub.find('appearance').text, @@ -91,7 +90,7 @@ def import_styles(url): `url` defaults to the official BJCP XML styleguide. Each subcategory is converted to JSON and then put to a couchdb. The _id of - the subsequent document will be `style_`, i.e., style_1A. + the subsequent document will be ``, i.e., 1A. If the style already exists in the database, it will be skipped. """ db = get_db() @@ -167,3 +166,15 @@ def index(): @login_required def info(id): return render_template('styles/info.html', style=get_doc_or_404(id)) + + +@bp.route('/info//recipes') +def recipes(id): + style = get_doc_or_404(id) + view = get_view('_design/recipes', 'by-style') + try: + rows = view(include_docs=True, descending=True, key=id)['rows'] + except requests.exceptions.HTTPError: + abort(400) + + return render_template('styles/recipes.html', style=style, rows=rows) diff --git a/src/humulus/templates/recipes/info.html b/src/humulus/templates/recipes/info.html index d925b69..d55e07c 100644 --- a/src/humulus/templates/recipes/info.html +++ b/src/humulus/templates/recipes/info.html @@ -22,11 +22,7 @@

{{ recipe.name }}

{% if style %}
- {% if session.logged_in %} -

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

- {% else %} -

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

- {% endif %} +

{{ style._id }} {{ style.name }}

{% endif %} {#- diff --git a/src/humulus/templates/styles/index.html b/src/humulus/templates/styles/index.html index 8d3718b..e4a78e6 100644 --- a/src/humulus/templates/styles/index.html +++ b/src/humulus/templates/styles/index.html @@ -45,7 +45,7 @@ {% for row in rows %} - {{ row.doc.id }} + {{ row.doc._id }} {{ row.doc.name }} {% endfor %} diff --git a/src/humulus/templates/styles/info.html b/src/humulus/templates/styles/info.html index 88a6ba7..562c689 100644 --- a/src/humulus/templates/styles/info.html +++ b/src/humulus/templates/styles/info.html @@ -26,10 +26,11 @@ {% extends '_base.html' %} -{% block title %}{{ style.id }} {{ style.name }}{% endblock %} +{% block title %}{{ style._id }} {{ style.name }}{% endblock %} {% block body %} -

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

+

{{ style._id }} {{ style.name }}

+

View recipes using this style

{{ render_detail(style.impression, 'Overall Impression') }} {{ render_detail(style.appearance, 'Appearance') }} {{ render_detail(style.aroma, 'Aroma') }} diff --git a/src/humulus/templates/styles/recipes.html b/src/humulus/templates/styles/recipes.html new file mode 100644 index 0000000..55e86b2 --- /dev/null +++ b/src/humulus/templates/styles/recipes.html @@ -0,0 +1,39 @@ +{#- + Copyright 2019 Mike Shoup + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-#} + +{% extends '_base.html' %} +{% block title %}Recipes for {{ style._id }} {{ style.name }}{% endblock %} + +{% block body %} +{% if session.logged_in %} + +{% else %} +

Recipes for {{ style._id }} {{ style.name }}

+{% endif %} + +
+ + + + + {% for row in rows %} + + + + {% endfor %} +
Name
{{ row.doc.name }}
+
+{% endblock %} diff --git a/tests/conftest.py b/tests/conftest.py index 48590a9..52bd0c3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,7 +78,7 @@ def app(): 'name': 'Awesome Beer', 'notes': 'This is a test beer that contains most possible fields.', 'volume': '2.5', - 'style': 'style_1A', + 'style': '1A', 'fermentables': [ { 'name': '2row', @@ -127,7 +127,7 @@ def app(): # Add a test style put_doc({'$type': 'style', - '_id': 'style_1A', + '_id': '1A', 'abv': {'high': '100', 'low': '0'}, 'appearance': 'Good looking', 'aroma': 'Smelly', diff --git a/tests/test_recipes.py b/tests/test_recipes.py index a0b0e2b..6b1a1fd 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -136,7 +136,7 @@ def test_create(client, app, auth): 'name': 'Test', 'notes': 'Test', 'volume': '5.5', - 'style': 'style_1A' + 'style': '1A' } response = client.post('/recipes/create', data=data) assert response.status_code == 302 @@ -148,7 +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' + assert doc['style'] == '1A' def test_update(client, app, auth): @@ -296,7 +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' + recipe.style.data = '1A' assert recipe.doc == { 'name': 'Test', @@ -307,7 +307,7 @@ def test_recipe_form_doc(app): 'fermentables': [], 'hops': [], '$type': 'recipe', - 'style': 'style_1A' + 'style': '1A' } ferm = FermentableForm() @@ -340,7 +340,7 @@ def test_recipe_form_doc(app): 'volume': '5.5', 'notes': 'This is a test', '$type': 'recipe', - 'style': 'style_1A', + 'style': '1A', 'fermentables': [{ 'name': 'Test', 'type': 'Grain', diff --git a/tests/test_styles.py b/tests/test_styles.py index 9ea0b6c..23c3933 100644 --- a/tests/test_styles.py +++ b/tests/test_styles.py @@ -84,9 +84,8 @@ TEST_XML = ''' def test_sub_to_doc(): assert sub_to_doc(ET.fromstring(COMPLETE_STYLE)) == { - '_id': 'style_1A', + '_id': '1A', '$type': 'style', - 'id': '1A', 'name': 'Test Style', 'aroma': 'Smelly', 'appearance': 'Good looking', @@ -107,9 +106,8 @@ def test_sub_to_doc(): } assert sub_to_doc(ET.fromstring(INCOMPLETE_STYLE)) == { - '_id': 'style_2B', + '_id': '2B', '$type': 'style', - 'id': '2B', 'name': 'Test Style', 'aroma': 'Smelly', 'appearance': 'Good looking', @@ -148,14 +146,13 @@ def test_import_styles(monkeypatch): import_styles(None) assert PutRecorder.doc == {'$type': 'style', - '_id': 'style_1A', + '_id': '1A', 'abv': {'high': '100', 'low': '0'}, 'appearance': 'Good looking', 'aroma': 'Smelly', 'fg': {'high': '1.2', 'low': '1.0'}, 'flavor': 'Good tasting', 'ibu': {'high': '100', 'low': '0'}, - 'id': '1A', 'impression': 'Refreshing', 'mouthfeel': 'Good feeling', 'name': 'Test Style', @@ -163,7 +160,7 @@ def test_import_styles(monkeypatch): 'srm': {'high': '100', 'low': '0'} } - MockDB.db = {'style_1A': ''} + MockDB.db = {'1A': ''} PutRecorder.doc = None import_styles(None) assert PutRecorder.doc is None @@ -188,7 +185,7 @@ def test_get_styles_choices(app): styles = get_styles_list() assert styles == [ ['', ''], - ['style_1A', '1A Test Style'] + ['1A', '1A Test Style'] ] @@ -213,12 +210,12 @@ def test_index(auth, client): def test_info(auth, client): """Test success in retrieving a style's info page""" # Test not logged in - response = client.get('/styles/info/style_1A') + response = client.get('/styles/info/1A') assert response.status_code == 302 # Login and test auth.login() - response = client.get('/styles/info/style_1A') + response = client.get('/styles/info/1A') assert response.status_code == 200 assert b'1A' in response.data assert b'Test Style' in response.data