Added additional database fields and properties.
Much calculation of efficiency and stuff now.
This commit is contained in:
parent
570ede627f
commit
90bf91c3ed
@ -7,6 +7,7 @@ from beer.models import Batch, Recipe, Mash, MashStep, \
|
|||||||
from yeast.models import Yeast
|
from yeast.models import Yeast
|
||||||
|
|
||||||
from config.extras import BREWFATHER_APP_ROOT
|
from config.extras import BREWFATHER_APP_ROOT
|
||||||
|
from beer.extras import plato_sg
|
||||||
|
|
||||||
|
|
||||||
class SampleInline(admin.TabularInline):
|
class SampleInline(admin.TabularInline):
|
||||||
@ -36,7 +37,7 @@ class StrainInline(admin.TabularInline):
|
|||||||
|
|
||||||
@admin.register(Recipe)
|
@admin.register(Recipe)
|
||||||
class RecipeAdmin(admin.ModelAdmin):
|
class RecipeAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name']
|
list_display = ['name', 'total_extract_kg']
|
||||||
inlines = [
|
inlines = [
|
||||||
FermentableInline,
|
FermentableInline,
|
||||||
HopInline,
|
HopInline,
|
||||||
@ -47,11 +48,40 @@ class RecipeAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(Batch)
|
@admin.register(Batch)
|
||||||
class BeerBatchAdmin(admin.ModelAdmin):
|
class BeerBatchAdmin(admin.ModelAdmin):
|
||||||
list_display = ['brewfather_id', 'batch_url']
|
list_display = [
|
||||||
|
'brewfather_id',
|
||||||
|
'batch_url',
|
||||||
|
'brewhouse_efficiency',
|
||||||
|
'conversion_efficiency',
|
||||||
|
]
|
||||||
inlines = [
|
inlines = [
|
||||||
SampleInline,
|
SampleInline,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
fieldsets = [
|
||||||
|
[None, {
|
||||||
|
'fields': [
|
||||||
|
('brewfather_id', 'brewfather_num', 'brewfather_name'),
|
||||||
|
'recipe',
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
['Mash', {
|
||||||
|
'fields': ['first_runnings', 'mash_ph'],
|
||||||
|
}],
|
||||||
|
['Boil', {
|
||||||
|
'fields': [
|
||||||
|
('pre_boil_vol', 'pre_boil_sg'),
|
||||||
|
('post_boil_vol', 'post_boil_sg'),
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['Ferment', {
|
||||||
|
'fields': [
|
||||||
|
('fermenter_topup_vol', 'fermenter_vol'),
|
||||||
|
('original_sg', 'final_sg'),
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
]
|
||||||
|
|
||||||
def batch_url(self, obj):
|
def batch_url(self, obj):
|
||||||
url_string = ('<a href="{root}/tabs/batches/batch/{batch_id}">'
|
url_string = ('<a href="{root}/tabs/batches/batch/{batch_id}">'
|
||||||
'Brewfather Batch ID: {batch_id}</a>')
|
'Brewfather Batch ID: {batch_id}</a>')
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-06-25 15:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('beer', '0013_batch_first_runnings_equipmentprofile_mash_ratio'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='post_boil_sg',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=6, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='post_boil_vol',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fermentable',
|
||||||
|
name='moisture',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, default=0.04, max_digits=6, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fermentable',
|
||||||
|
name='potential',
|
||||||
|
field=models.DecimalField(decimal_places=4, default=0.8, max_digits=6),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,53 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2024-06-26 14:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('beer', '0014_batch_post_boil_sg_batch_post_boil_vol_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='fermenter_topup_vol',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='fermenter_vol',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='final_sg',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=5, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='mash_ph',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=3, max_digits=4, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='original_sg',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=5, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='pre_boil_sg',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=5, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='batch',
|
||||||
|
name='pre_boil_vol',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=8, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='batch',
|
||||||
|
name='post_boil_sg',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=4, max_digits=5, null=True),
|
||||||
|
),
|
||||||
|
]
|
169
beer/models.py
169
beer/models.py
@ -5,6 +5,7 @@ from django_cryptography.fields import encrypt
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from config.extras import BREWFATHER_APP_ROOT
|
from config.extras import BREWFATHER_APP_ROOT
|
||||||
|
from beer.extras import sg_plato, plato_sg, kg_extract, convert
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -36,47 +37,70 @@ class Batch(CustomModel):
|
|||||||
recipe = models.OneToOneField(
|
recipe = models.OneToOneField(
|
||||||
'Recipe', on_delete=models.CASCADE, default=1)
|
'Recipe', on_delete=models.CASCADE, default=1)
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Brewday Measurements
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Mash
|
||||||
first_runnings = models.DecimalField(
|
first_runnings = models.DecimalField(
|
||||||
max_digits=8, decimal_places=4, null=True, blank=True)
|
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||||
|
mash_ph = models.DecimalField(
|
||||||
|
max_digits=4, decimal_places=3, null=True, blank=True)
|
||||||
|
|
||||||
# Batch measurements to add:
|
## Boil
|
||||||
# - Mash pH
|
pre_boil_vol = models.DecimalField(
|
||||||
# - First Runnings gravity (include lookup table fo rmash thickness)
|
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||||
# - Boil Vol
|
pre_boil_sg = models.DecimalField(
|
||||||
# - Pre-Boil Gravity
|
max_digits=5, decimal_places=4, null=True, blank=True)
|
||||||
# - Post-Boil Vol
|
post_boil_vol = models.DecimalField(
|
||||||
# - Post-Boil Gravity
|
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||||
# - Original Gravity
|
post_boil_sg = models.DecimalField(
|
||||||
# - Final Gravity
|
max_digits=5, decimal_places=4, null=True, blank=True)
|
||||||
# - Fermenter Top-Up
|
|
||||||
# - Fermenter Vol
|
## Ferment
|
||||||
|
fermenter_topup_vol = models.DecimalField(
|
||||||
|
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||||
|
fermenter_vol = models.DecimalField(
|
||||||
|
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||||
|
original_sg = models.DecimalField(
|
||||||
|
max_digits=5, decimal_places=4, null=True, blank=True)
|
||||||
|
final_sg = models.DecimalField(
|
||||||
|
max_digits=5, decimal_places=4, null=True, blank=True)
|
||||||
|
|
||||||
# Properties Needed: (https://braukaiser.com/wiki/index.php/Troubleshooting_Brewhouse_Efficiency)
|
# Properties Needed: (https://braukaiser.com/wiki/index.php/Troubleshooting_Brewhouse_Efficiency)
|
||||||
# - Conversion Efficiency
|
|
||||||
# - Mash Efficiency
|
# - Mash Efficiency
|
||||||
# - Brewhouse Efficiency
|
|
||||||
# kettle extract weight in kg = volume in liter * SG * Plato / 100
|
|
||||||
# brewhouse efficiency in % = 100% * kettle extract weight in kg / extract in grist in kg
|
|
||||||
# - ABV
|
# - ABV
|
||||||
# - Attenuation
|
# - Attenuation
|
||||||
# - Actual Boil-Off Rate
|
# - Actual Boil-Off Rate
|
||||||
# - Actual Trub/Chiller Loss
|
# - Actual Trub/Chiller Loss
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Batch Stats
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
@property
|
||||||
|
def brewhouse_efficiency(self):
|
||||||
|
try:
|
||||||
|
return round(self.boil_extract_kg/self.recipe.total_extract_kg,4)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def boil_extract_kg(self):
|
||||||
|
return kg_extract(float(self.post_boil_vol)*.96, self.post_boil_sg)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def conversion_efficiency(self):
|
def conversion_efficiency(self):
|
||||||
""" Calculate conversion efficiency of mash."""
|
""" Calculate conversion efficiency of mash."""
|
||||||
strike_volume = (self.recipe.equipment.mash_ratio
|
if self.first_runnings is None or self.recipe.fermentable_weight_kg == 0:
|
||||||
* self.recipe.fermentable_weight)
|
return '-'
|
||||||
|
|
||||||
pot_yield = 0
|
return round((sg_plato(self.first_runnings)/self.recipe.fw_max)
|
||||||
for ferm in self.recipefermentable_set.all():
|
* (100-self.recipe.fw_max) / (100-sg_plato(self.first_runnings))
|
||||||
pot_yield += ferm.extract_potential * ferm.quantity
|
, 4)
|
||||||
|
|
||||||
pot_yield = pot_yield / self.recipe.fermentable_weight
|
@property
|
||||||
fw_max = pot_yield / (strike_volume+pot_yield)
|
def boil_off_calcualted(self):
|
||||||
|
return float(self.pre_boil_vol - self.post_boil_vol) * .96
|
||||||
return (100 * (self.first_runnings/fw_max)
|
|
||||||
* (100-fw_max) / (100-self.first_runnings))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brewfather_url(self):
|
def brewfather_url(self):
|
||||||
@ -157,16 +181,41 @@ class Recipe(CustomModel):
|
|||||||
verbose_name_plural = 'Recipes'
|
verbose_name_plural = 'Recipes'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fermentable_weight(self):
|
def fw_max(self):
|
||||||
|
potential = 0
|
||||||
|
weight = 0
|
||||||
|
for ferm in self.recipefermentable_set.all():
|
||||||
|
potential += ferm.fermentable.extract_percent*float(ferm.quantity)
|
||||||
|
weight += float(ferm.quantity)
|
||||||
|
|
||||||
|
e_grain = potential / weight
|
||||||
|
|
||||||
|
ratio = float(self.equipment.mash_ratio)
|
||||||
|
return 100*e_grain / (ratio+e_grain)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fermentable_weight_kg(self):
|
||||||
"""Weight of all fermentables attached to recipe."""
|
"""Weight of all fermentables attached to recipe."""
|
||||||
aggregate = self.recipefermentable_set.all().aggregate(Sum('quantity'))
|
aggregate = self.recipefermentable_set.all().aggregate(Sum('quantity'))
|
||||||
return aggregate['quantity__sum']
|
|
||||||
|
if aggregate['quantity__sum']:
|
||||||
|
return aggregate['quantity__sum']
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_extract_kg(self):
|
||||||
|
extract = 0
|
||||||
|
for f in self.recipefermentable_set.all():
|
||||||
|
extract += f.extract_weight_kg
|
||||||
|
|
||||||
|
return extract
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hop_weight(self):
|
def hop_weight(self):
|
||||||
"""Weight of all fermentables attached to recipe."""
|
"""Weight of all fermentables attached to recipe."""
|
||||||
aggregate = self.recipehop_set.all().aggregate(Sum('quantity'))
|
aggregate = self.recipehop_set.all().aggregate(Sum('quantity'))
|
||||||
return aggregate['quantity__sum']
|
return float(aggregate['quantity__sum']) * 0.0352739619
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def final_volume(self):
|
def final_volume(self):
|
||||||
@ -179,44 +228,43 @@ class Recipe(CustomModel):
|
|||||||
@property
|
@property
|
||||||
def sugar_yield(self):
|
def sugar_yield(self):
|
||||||
"""Return point yield of all non-mashed ingredients."""
|
"""Return point yield of all non-mashed ingredients."""
|
||||||
ferm_yield = 0
|
|
||||||
|
|
||||||
ferms = self.recipefermentable_set.all().select_related('fermentable')
|
ferms = self.recipefermentable_set.all().select_related('fermentable')
|
||||||
sugars = ferms.filter(fermentable__fermentable_type=3)
|
sugars = ferms.filter(fermentable__fermentable_type=3)
|
||||||
|
|
||||||
|
ferm_yield = 0
|
||||||
for f in sugars:
|
for f in sugars:
|
||||||
ferm_yield += f.ferm_yield
|
ferm_yield += f.extract_weight_kg
|
||||||
|
|
||||||
return float(ferm_yield)
|
return float(ferm_yield)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mash_yield(self):
|
def mash_yield(self):
|
||||||
"""Return point yield of all mashed ingredients."""
|
"""Return point yield of all mashed ingredients."""
|
||||||
mash_yield = 0
|
|
||||||
|
|
||||||
ferms = self.recipefermentable_set.all().select_related('fermentable')
|
ferms = self.recipefermentable_set.all().select_related('fermentable')
|
||||||
# Is not sugar (3)
|
|
||||||
mashed = ferms.filter(~Q(fermentable__fermentable_type=3))
|
mashed = ferms.filter(~Q(fermentable__fermentable_type=3))
|
||||||
|
|
||||||
|
mash_yield = 0
|
||||||
for f in mashed:
|
for f in mashed:
|
||||||
mash_yield += f.ferm_yield
|
mash_yield += f.extract_weight_kg * float(self.efficiency/100)
|
||||||
|
|
||||||
return float(mash_yield)
|
return float(mash_yield)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def original_sg(self):
|
def original_sg(self):
|
||||||
"""Return original gravity."""
|
"""Return original gravity."""
|
||||||
total_yield = self.sugar_yield + self.mash_yield
|
total_extract = self.sugar_yield + self.mash_yield
|
||||||
gravity_points = total_yield/self.final_volume
|
plato = 100 * total_extract / (self.final_volume + total_extract)
|
||||||
return round(1 + gravity_points/1000, 3)
|
return round(plato_sg(plato), 3)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pre_boil_sg(self):
|
def pre_boil_sg(self):
|
||||||
"""Return pre-boil gravity."""
|
"""Return pre-boil gravity."""
|
||||||
total_yield = self.sugar_yield + self.mash_yield
|
total_extract = self.sugar_yield + self.mash_yield
|
||||||
total_water = self.final_volume+self.boil_off_gph
|
total_water = self.final_volume+self.boil_off_gph
|
||||||
gravity_points = total_yield/total_water
|
plato = 100 * total_extract / (total_water + total_extract)
|
||||||
return round(1 + gravity_points/1000, 3)
|
return round(plato_sg(plato), 3)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hop_water_loss(self):
|
def hop_water_loss(self):
|
||||||
@ -320,7 +368,8 @@ class Fermentable(CustomIngredient):
|
|||||||
fermentable_type = models.IntegerField(choices=types, default=1)
|
fermentable_type = models.IntegerField(choices=types, default=1)
|
||||||
diastatic_power = models.DecimalField(
|
diastatic_power = models.DecimalField(
|
||||||
max_digits=6, decimal_places=4, null=True, blank=True)
|
max_digits=6, decimal_places=4, null=True, blank=True)
|
||||||
potential = models.DecimalField(max_digits=6, decimal_places=4)
|
potential = models.DecimalField(
|
||||||
|
max_digits=6, decimal_places=4, default=0.80)
|
||||||
protein = models.DecimalField(
|
protein = models.DecimalField(
|
||||||
max_digits=6, decimal_places=4, null=True, blank=True)
|
max_digits=6, decimal_places=4, null=True, blank=True)
|
||||||
attenuation = models.DecimalField(
|
attenuation = models.DecimalField(
|
||||||
@ -330,11 +379,15 @@ class Fermentable(CustomIngredient):
|
|||||||
max_in_batch = models.DecimalField(
|
max_in_batch = models.DecimalField(
|
||||||
max_digits=6, decimal_places=4, null=True, blank=True)
|
max_digits=6, decimal_places=4, null=True, blank=True)
|
||||||
moisture = models.DecimalField(
|
moisture = models.DecimalField(
|
||||||
max_digits=6, decimal_places=4, null=True, blank=True)
|
max_digits=6, decimal_places=4, null=True, blank=True, default=0.04)
|
||||||
non_fermentable = models.BooleanField(null=True, blank=True)
|
non_fermentable = models.BooleanField(null=True, blank=True)
|
||||||
ibu_per_unit = models.DecimalField(
|
ibu_per_unit = models.DecimalField(
|
||||||
max_digits=6, decimal_places=4, default=0)
|
max_digits=6, decimal_places=4, default=0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extract_percent(self):
|
||||||
|
return ((float(self.potential)-1)*1000/46.17) * (1-float(self.moisture)/100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -345,32 +398,29 @@ class RecipeFermentable(CustomModel):
|
|||||||
quantity = models.DecimalField(max_digits=6, decimal_places=4)
|
quantity = models.DecimalField(max_digits=6, decimal_places=4)
|
||||||
|
|
||||||
# Properties Needed:
|
# Properties Needed:
|
||||||
# - Extract Potential (1-moisture percent as decimal) * potential
|
|
||||||
# - The weight of extract in the grist
|
# - The weight of extract in the grist
|
||||||
# extract in grist in kg = weight of grist in kg * extract potential
|
# extract in grist in kg = weight of grist in kg * extract potential
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extract_potential(self):
|
def quantity_display(self):
|
||||||
return .8 * .96
|
return convert(self.quantity, 'kg', 'lb')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extract_weight_kg(self):
|
||||||
|
return float(self.quantity) * self.fermentable.extract_percent
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percent(self):
|
def percent(self):
|
||||||
return float(100 * self.quantity / self.recipe.fermentable_weight)
|
return float(100 * self.quantity / self.recipe.fermentable_weight_kg)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lovibond_contributed(self):
|
def lovibond_contributed(self):
|
||||||
srm_calc = (float(self.fermentable.lovibond)
|
srm_calc = (float(self.fermentable.lovibond)
|
||||||
* float(self.quantity) / self.recipe.final_volume)
|
* convert(self.quantity, 'kg', 'lb')
|
||||||
|
/ convert(self.recipe.final_volume, 'l', 'gal'))
|
||||||
|
|
||||||
return round(srm_calc, 1)
|
return round(srm_calc, 1)
|
||||||
|
|
||||||
@property
|
|
||||||
def ferm_yield(self):
|
|
||||||
potential_yield = self.quantity * (self.fermentable.potential-1) * 1000
|
|
||||||
|
|
||||||
if self.fermentable.fermentable_type == 3:
|
|
||||||
return potential_yield
|
|
||||||
else:
|
|
||||||
return potential_yield * (self.recipe.efficiency/100)
|
|
||||||
|
|
||||||
|
|
||||||
class Hop(CustomIngredient):
|
class Hop(CustomIngredient):
|
||||||
uses = {
|
uses = {
|
||||||
@ -409,6 +459,11 @@ class RecipeHop(CustomModel):
|
|||||||
time = models.IntegerField(default=60, validators=[MinValueValidator(0)])
|
time = models.IntegerField(default=60, validators=[MinValueValidator(0)])
|
||||||
use = models.IntegerField(choices=uses, default=1)
|
use = models.IntegerField(choices=uses, default=1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def quantity_display(self):
|
||||||
|
"""Convert grams to ounces."""
|
||||||
|
return convert(self.quantity, 'g', 'oz')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ibu_tinseth(self):
|
def ibu_tinseth(self):
|
||||||
type_bonus = {
|
type_bonus = {
|
||||||
@ -424,8 +479,8 @@ class RecipeHop(CustomModel):
|
|||||||
+ (self.recipe.original_sg-1)) / 2)
|
+ (self.recipe.original_sg-1)) / 2)
|
||||||
|
|
||||||
if self.use == 1:
|
if self.use == 1:
|
||||||
conc = (float((self.hop.alpha/100) * self.quantity)
|
conc = (float((self.hop.alpha/100)) * float(self.quantity)*0.0352739619
|
||||||
* 7490/self.recipe.final_volume)
|
* 7490/convert(self.recipe.final_volume, 'l', 'gal'))
|
||||||
util = (hop_bonus*1.65*0.000125**average_wort_sg
|
util = (hop_bonus*1.65*0.000125**average_wort_sg
|
||||||
* ((1-2.71828182845904**(-0.04*self.time)) / 4.15))
|
* ((1-2.71828182845904**(-0.04*self.time)) / 4.15))
|
||||||
ibu = conc * util
|
ibu = conc * util
|
||||||
|
@ -88,8 +88,8 @@ font-size: .8em;
|
|||||||
|
|
||||||
<caption>
|
<caption>
|
||||||
<p class="text-end smaller">
|
<p class="text-end smaller">
|
||||||
Pre-Boil Gravity: <b>{{ recipe.pre_boil_sg }}</b><br>
|
Pre-Boil Gravity: <b>{{ recipe.pre_boil_sg|floatformat:3 }}</b><br>
|
||||||
Original Gravity: <b>{{ recipe.original_sg }}</b><br>
|
Original Gravity: <b>{{ recipe.original_sg|floatformat:3 }}</b><br>
|
||||||
Color: <b>{{ recipe.srm|floatformat:0 }} SRM</b>
|
Color: <b>{{ recipe.srm|floatformat:0 }} SRM</b>
|
||||||
</p>
|
</p>
|
||||||
</caption>
|
</caption>
|
||||||
@ -97,9 +97,9 @@ font-size: .8em;
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for f in recipe.recipefermentable_set.all %}
|
{% for f in recipe.recipefermentable_set.all %}
|
||||||
<tr onclick="window.location='{% url 'beer:update_fermentable' f.fermentable.id %}';">
|
<tr onclick="window.location='{% url 'beer:update_fermentable' f.fermentable.id %}';">
|
||||||
<td>{{ f.quantity|floatformat:2 }} lb</td>
|
<td>{{ f.quantity_display|floatformat:2 }} lb</td>
|
||||||
<td>{{ f.fermentable.name }}<br>
|
<td>{{ f.fermentable.name }}<br>
|
||||||
<span class="text-muted">{{ f.fermentable.get_fermentable_type_display }} {{ f.srm }} SRM</span>
|
<span class="text-muted">{{ f.fermentable.get_fermentable_type_display }} {{ f.lovibond_contributed }} L</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ f.percent|floatformat:1 }} %</td>
|
<td>{{ f.percent|floatformat:1 }} %</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -132,7 +132,7 @@ font-size: .8em;
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for h in recipe.recipehop_set.all %}
|
{% for h in recipe.recipehop_set.all %}
|
||||||
<tr onclick="window.location='{% url 'beer:update_hop' h.hop.id %}';">
|
<tr onclick="window.location='{% url 'beer:update_hop' h.hop.id %}';">
|
||||||
<td>{{ h.quantity|floatformat:2 }} oz</td>
|
<td>{{ h.quantity_display|floatformat:2 }} oz</td>
|
||||||
<td>{{ h.hop.name }} {{ h.hop.alpha|floatformat:1 }} %<br>
|
<td>{{ h.hop.name }} {{ h.hop.alpha|floatformat:1 }} %<br>
|
||||||
<span class="text-muted">{{ h.hop.get_hop_type_display }} {{ h.ibu_tinseth|floatformat:1 }} IBU</span>
|
<span class="text-muted">{{ h.hop.get_hop_type_display }} {{ h.ibu_tinseth|floatformat:1 }} IBU</span>
|
||||||
</td>
|
</td>
|
||||||
|
Loading…
Reference in New Issue
Block a user