1
0
Fork 0
mirror of https://github.com/shouptech/humulus.git synced 2026-02-03 15:19:43 +00:00

Allow Recipe Types (#16)

* Adds a recipe type field for recipe form. (Closes #7)
* Abort w/ 400 is view is not found
This commit is contained in:
Emma 2019-07-07 08:31:52 -06:00 committed by GitHub
parent 221ddbafe0
commit e11fd79deb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 67 additions and 10 deletions

View file

@ -10,6 +10,9 @@
}, },
"by-volume": { "by-volume": {
"map": "function (doc) {\n if (doc.$type == \"recipe\" && doc.volume && doc.name) {\n emit(doc.volume, doc.name)\n }\n}" "map": "function (doc) {\n if (doc.$type == \"recipe\" && doc.volume && doc.name) {\n emit(doc.volume, doc.name)\n }\n}"
},
"by-type": {
"map": "function (doc) {\n if (doc.$type == \"recipe\" && doc.type && doc.name) {\n emit(doc.type, doc.name)\n }\n}"
} }
}, },
"lists": {}, "lists": {},

View file

@ -17,7 +17,8 @@
import json import json
from decimal import Decimal from decimal import Decimal
from flask import (Blueprint, flash, redirect, render_template, jsonify, import requests
from flask import (abort, Blueprint, flash, redirect, render_template, jsonify,
request, url_for) request, url_for)
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired from flask_wtf.file import FileField, FileRequired
@ -148,6 +149,11 @@ class YeastForm(Form):
class RecipeForm(FlaskForm): class RecipeForm(FlaskForm):
"""Form for recipes.""" """Form for recipes."""
name = StringField('Name', validators=[DataRequired()]) name = StringField('Name', validators=[DataRequired()])
type = SelectField('Type', default='',
choices=[(c, c) for c in ['All-Grain',
'Partial Extract',
'Extract']],
validators=[Optional()])
efficiency = DecimalField('Batch Efficiency (%)', efficiency = DecimalField('Batch Efficiency (%)',
validators=[DataRequired()]) validators=[DataRequired()])
volume = DecimalField('Batch Volume (gal)', validators=[DataRequired()]) volume = DecimalField('Batch Volume (gal)', validators=[DataRequired()])
@ -176,6 +182,7 @@ class RecipeForm(FlaskForm):
'volume': str(self.volume.data), 'volume': str(self.volume.data),
'notes': self.notes.data, 'notes': self.notes.data,
'$type': 'recipe', '$type': 'recipe',
'type': self.type.data
} }
recipe['fermentables'] = [f.doc for f in self.fermentables] recipe['fermentables'] = [f.doc for f in self.fermentables]
@ -191,6 +198,7 @@ class RecipeForm(FlaskForm):
def copyfrom(self, data): def copyfrom(self, data):
"""Copies from a dictionary (data) into the current object""" """Copies from a dictionary (data) into the current object"""
self.name.data = data['name'] self.name.data = data['name']
self.type.data = data['type']
self.efficiency.data = Decimal(data['efficiency']) self.efficiency.data = Decimal(data['efficiency'])
self.volume.data = Decimal(data['volume']) self.volume.data = Decimal(data['volume'])
self.notes.data = data['notes'] self.notes.data = data['notes']
@ -255,16 +263,16 @@ def index():
['true', 'yes'] ['true', 'yes']
) )
sort_by = request.args.get('sort_by', default='name', type=str) sort_by = request.args.get('sort_by', default='name', type=str)
if sort_by == 'date':
view = get_view('_design/recipes', 'by-date') view = get_view('_design/recipes', 'by-{}'.format(sort_by))
elif sort_by == 'volume': try:
view = get_view('_design/recipes', 'by-volume') rows = view(include_docs=True, descending=descending)['rows']
else: except requests.exceptions.HTTPError:
view = get_view('_design/recipes', 'by-name') abort(400)
return render_template( return render_template(
'recipes/index.html', 'recipes/index.html',
rows=view(include_docs=True, descending=descending)['rows'], rows=rows,
descending=descending, descending=descending,
sort_by=sort_by sort_by=sort_by
) )

View file

@ -26,8 +26,9 @@
-#} -#}
<div class="row"> <div class="row">
<div class="col-sm-6">{{ render_field_with_errors(form.name) }}</div> <div class="col-sm-6">{{ render_field_with_errors(form.name) }}</div>
<div class="col-sm-3">{{ render_field_with_errors(form.efficiency, 'ingredient-field') }}</div> <div class="col-sm-2">{{ render_field_with_errors(form.type) }}</div>
<div class="col-sm-3">{{ render_field_with_errors(form.volume, 'ingredient-field') }}</div> <div class="col-sm-2">{{ render_field_with_errors(form.efficiency, 'ingredient-field') }}</div>
<div class="col-sm-2">{{ render_field_with_errors(form.volume, 'ingredient-field') }}</div>
</div> </div>
{#- {#-
Fermentable Ingredients Fermentable Ingredients

View file

@ -49,6 +49,15 @@
<a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='volume') }}">Batch Size</a> <a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='volume') }}">Batch Size</a>
{% endif %} {% endif %}
</th> </th>
<th>
{% if sort_by == 'type' and descending %}
<a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='type') }}">Type &darr;</a>
{% elif sort_by == 'type' %}
<a class="text-dark" href="{{ url_for('recipes.index', descending='true', sort_by='type') }}">Type &uarr;</a>
{% else %}
<a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='type') }}">Type</a>
{% endif %}
</th>
<th> <th>
{% if sort_by == 'date' and descending %} {% if sort_by == 'date' and descending %}
<a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='date') }}">Created On &darr;</a> <a class="text-dark" href="{{ url_for('recipes.index', descending='false', sort_by='date') }}">Created On &darr;</a>
@ -64,6 +73,7 @@
<tr> <tr>
<td><a href="{{ url_for('recipes.info', id=row.id) }}">{{ row.doc.name }}</a></td> <td><a href="{{ url_for('recipes.info', id=row.id) }}">{{ row.doc.name }}</a></td>
<td>{{ row.doc.volume }} gal.</td> <td>{{ row.doc.volume }} gal.</td>
<td>{{ row.doc.type }}</td>
<td>{{ moment(row.doc.created) }}</td> <td>{{ moment(row.doc.created) }}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -28,6 +28,8 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<dl> <dl>
<dt>Recipe Type</dt>
<dd>{{ recipe.type }}</dd>
<dt>Batch Efficiency</dt> <dt>Batch Efficiency</dt>
<dd>{{ recipe.efficiency|int }}%</dd> <dd>{{ recipe.efficiency|int }}%</dd>
<dt>Batch Volume</dt> <dt>Batch Volume</dt>

View file

@ -44,6 +44,7 @@ def app():
put_doc({ put_doc({
'_id': 'awesome-lager', '_id': 'awesome-lager',
'$type': 'recipe', '$type': 'recipe',
'type': 'All-Grain',
'efficiency': '65', 'efficiency': '65',
'name': 'Awesome Lager', 'name': 'Awesome Lager',
'notes': 'Test', 'notes': 'Test',
@ -56,6 +57,7 @@ def app():
'$type': 'recipe', '$type': 'recipe',
'efficiency': '75', 'efficiency': '75',
'name': 'Partial Beer', 'name': 'Partial Beer',
'type': 'Extract',
'notes': 'Contains only required fields for yeast.', 'notes': 'Contains only required fields for yeast.',
'volume': '3.5', 'volume': '3.5',
'fermentables': [], 'fermentables': [],
@ -70,6 +72,7 @@ def app():
'_id': 'full-recipe', '_id': 'full-recipe',
'$type': 'recipe', '$type': 'recipe',
'efficiency': '78', 'efficiency': '78',
'type': 'All-Grain',
'name': 'Awesome Beer', 'name': 'Awesome Beer',
'notes': 'This is a test beer that contains most possible fields.', 'notes': 'This is a test beer that contains most possible fields.',
'volume': '2.5', 'volume': '2.5',
@ -160,6 +163,7 @@ def sample_recipes():
return { return {
'lager': { 'lager': {
'efficiency': '72', 'efficiency': '72',
'type': 'All-Grain',
'fermentables': [ 'fermentables': [
{ {
'amount': '9.5', 'amount': '9.5',
@ -217,6 +221,7 @@ def sample_recipes():
}, },
'sweetstout': { 'sweetstout': {
'efficiency': '72', 'efficiency': '72',
'type': 'All-Grain',
'fermentables': [ 'fermentables': [
{ {
'amount': '2.75', 'amount': '2.75',

View file

@ -22,6 +22,10 @@ from humulus.recipes import FermentableForm, HopForm, RecipeForm, YeastForm
def test_index(client): def test_index(client):
"""Test success in retrieving index.""" """Test success in retrieving index."""
# Test for bad request
response = client.get('/recipes/?sort_by=foobar')
assert response.status_code == 400
# Verify defaults # Verify defaults
response = client.get('/recipes/') response = client.get('/recipes/')
assert response.status_code == 200 assert response.status_code == 200
@ -93,6 +97,27 @@ def test_index(client):
response.data response.data
) )
# Test sort by type ascending
response = client.get('/recipes/?descending=false&sort_by=type')
assert (
b'"/recipes/?descending=false&amp;sort_by=name">Name' in
response.data
)
assert (
b'"/recipes/?descending=true&amp;sort_by=type">Type &uarr;' in
response.data
)
# Test sort by type descending
response = client.get('/recipes/?descending=true&sort_by=type')
assert (
b'"/recipes/?descending=false&amp;sort_by=name">Name' in
response.data
)
assert (
b'"/recipes/?descending=false&amp;sort_by=type">Type &darr;' in
response.data
)
def test_create(client, app, auth): def test_create(client, app, auth):
"""Test success in creating a recipe document.""" """Test success in creating a recipe document."""
@ -253,10 +278,12 @@ def test_recipe_form_doc(app):
recipe.efficiency.data = Decimal('65') recipe.efficiency.data = Decimal('65')
recipe.volume.data = Decimal('5.5') recipe.volume.data = Decimal('5.5')
recipe.notes.data = 'This is a test' recipe.notes.data = 'This is a test'
recipe.type.data = 'All-Grain'
assert recipe.doc == { assert recipe.doc == {
'name': 'Test', 'name': 'Test',
'efficiency': '65', 'efficiency': '65',
'type': 'All-Grain',
'volume': '5.5', 'volume': '5.5',
'notes': 'This is a test', 'notes': 'This is a test',
'fermentables': [], 'fermentables': [],
@ -290,6 +317,7 @@ def test_recipe_form_doc(app):
assert recipe.doc == { assert recipe.doc == {
'name': 'Test', 'name': 'Test',
'efficiency': '65', 'efficiency': '65',
'type': 'All-Grain',
'volume': '5.5', 'volume': '5.5',
'notes': 'This is a test', 'notes': 'This is a test',
'$type': 'recipe', '$type': 'recipe',