diff --git a/.drone.yml b/.drone.yml index a36e5d7..9193253 100644 --- a/.drone.yml +++ b/.drone.yml @@ -46,6 +46,9 @@ steps: when: branch: - master + event: + exclude: + - pull_request - name: docker-release image: plugins/docker diff --git a/src/humulus/app.py b/src/humulus/app.py index 171b6ba..5bf78c5 100644 --- a/src/humulus/app.py +++ b/src/humulus/app.py @@ -46,4 +46,8 @@ def create_app(test_config=None): from . import auth app.register_blueprint(auth.bp) + # Register custom filters + from . import filters + filters.create_filters(app) + return app diff --git a/src/humulus/filters.py b/src/humulus/filters.py new file mode 100644 index 0000000..cf178d4 --- /dev/null +++ b/src/humulus/filters.py @@ -0,0 +1,118 @@ +"""This module contains filters used in rendering of Jinja templates.""" + +# 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. + +import math + + +def recipe_og(recipe): + """Returns a recipe's Original Gravity""" + if 'fermentables' not in recipe: + return '0.000' + points = 0 + grain_points = 0 + # Loop through fermentables, adding up points + for fermentable in recipe['fermentables']: + if fermentable['type'] == 'Grain': + grain_points += ( + float(fermentable['amount']) * float(fermentable['ppg']) + ) + else: + points += ( + float(fermentable['amount']) * float(fermentable['ppg']) + ) + points += grain_points * float(recipe['efficiency']) / 100 + return '{:.3f}'.format( + round(1 + points / (1000 * float(recipe['volume'])), 3) + ) + + +def recipe_fg(recipe): + """Returns a recipe's final gravity""" + if 'yeast' not in recipe or 'fermentables' not in recipe: + return '0.000' + og = float(recipe_og(recipe)) + og_delta = 0.0 + # Adjust original gravity by removing nonfermentables (i.e., Lactose) + for fermentable in recipe['fermentables']: + if fermentable['type'] == 'Non-fermentable': + og_delta += ( + float(fermentable['amount']) * float(fermentable['ppg']) / + (1000 * float(recipe['volume'])) + ) + attenuation = ( + ( + float(recipe['yeast']['low_attenuation']) + + float(recipe['yeast']['high_attenuation']) + ) / 200 + ) + return '{:.3f}'.format( + round(1 + (og - 1 - og_delta)*(1 - attenuation) + og_delta, 3) + ) + + +def recipe_ibu(recipe): + """Return a recipe's IBU""" + if 'hops' not in recipe: + return '0' + bigness = 1.65 * 0.000125**(float(recipe_og(recipe)) - 1) + ibu = 0.0 + for h in recipe['hops']: + mgl = ( + float(h['alpha']) * float(h['amount']) * 7490.0 / + (float(recipe['volume']) * 100.0) + ) + btf = (1 - math.exp(-0.04 * float(h['duration']))) / 4.15 + ibu += bigness * btf * mgl + return '{:.0f}'.format(ibu) + + +def recipe_ibu_ratio(recipe): + """Return a recipe's IBU ratio""" + if 'fermentables' not in recipe or 'hops' not in recipe: + return '0' + if len(recipe['fermentables']) == 0: + return '0' # Otherwise a divide by zero error will occur + og = float(recipe_og(recipe)) + ibu = float(recipe_ibu(recipe)) + return '{:.2f}'.format(round(0.001 * ibu / (og - 1), 2)) + + +def recipe_abv(recipe): + """Return a recipe's finished ABV""" + if 'fermentables' not in recipe or 'yeast' not in recipe: + return '0' + og = float(recipe_og(recipe)) + fg = float(recipe_fg(recipe)) + return '{:.1f}'.format(round((og - fg) * 131.25, 1)) + + +def recipe_srm(recipe): + """Return a recipe's SRM""" + if 'fermentables' not in recipe: + return '0' + mcu = 0 + for f in recipe['fermentables']: + mcu += float(f['amount']) * float(f['color']) / float(recipe['volume']) + return '{:.0f}'.format(1.4922 * (mcu**0.6859)) + + +def create_filters(app): + app.add_template_filter(recipe_og) + app.add_template_filter(recipe_fg) + app.add_template_filter(recipe_ibu) + app.add_template_filter(recipe_ibu_ratio) + app.add_template_filter(recipe_abv) + app.add_template_filter(recipe_srm) diff --git a/src/humulus/static/recipes.js b/src/humulus/static/recipes.js index 2e057b2..c6daad4 100644 --- a/src/humulus/static/recipes.js +++ b/src/humulus/static/recipes.js @@ -13,6 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. */ + +// unbinds and re-binds the change event +function rebindChangeEvents() { + $('.ingredient-field').unbind('change'); + $('.ingredient-field').change(displayAll); +} + // Correct all the indices for forms matching item. function adjustIndices(removedIndex, item) { var $forms = $(item); @@ -51,6 +58,7 @@ function removeForm($remButton, formClass, formsId) { var $fermsDiv = $(formsId); $fermsDiv.data('length', $fermsDiv.data('length') - 1); adjustIndices(removedIndex, formClass); + displayAll(); } // Remove a fermentable @@ -75,14 +83,14 @@ function addFerm() { // Name field '