Compare commits
3 Commits
3fd248d8b7
...
53b7a1b128
Author | SHA1 | Date | |
---|---|---|---|
53b7a1b128 | |||
e8c9196fc0 | |||
93b93a1448 |
@ -7,7 +7,6 @@ from beer.models import Batch, Recipe, Mash, MashStep, \
|
||||
from yeast.models import Yeast
|
||||
|
||||
from config.extras import BREWFATHER_APP_ROOT
|
||||
from beer.extras import plato_sg
|
||||
|
||||
|
||||
class SampleInline(admin.TabularInline):
|
||||
|
@ -0,0 +1,103 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-26 17:49
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('beer', '0015_batch_fermenter_topup_vol_batch_fermenter_vol_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelTableComment(
|
||||
name='equipmentprofile',
|
||||
table_comment='Volumes in liters and weights in kg.',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='batch',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='leaf_hop_trub',
|
||||
field=models.DecimalField(db_comment='liters/gram', decimal_places=10, default=0.0083454045, max_digits=12),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='pellet_hop_trub',
|
||||
field=models.DecimalField(db_comment='liters/gram', decimal_places=10, default=0.003338162, max_digits=12),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='fermentable',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='hop',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='mash',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='mashstep',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='misc',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipe',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipefermentable',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipehop',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipemisc',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='recipeyeast',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplier',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unit',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userprofile',
|
||||
name='created_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
||||
),
|
||||
]
|
@ -0,0 +1,68 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-26 18:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('beer', '0016_alter_equipmentprofile_table_comment_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='batch_volume',
|
||||
field=models.DecimalField(decimal_places=2, default=5.5, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='grain_absorption',
|
||||
field=models.DecimalField(decimal_places=2, default=0.12, help_text='liters/kilogram', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='hlt_deadspace',
|
||||
field=models.DecimalField(decimal_places=2, default=0.25, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='kettle_boil_rate',
|
||||
field=models.DecimalField(decimal_places=2, default=0.5, help_text='liters/hr', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='kettle_deadspace',
|
||||
field=models.DecimalField(decimal_places=2, default=0.25, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='kettle_plumbing_loss',
|
||||
field=models.DecimalField(decimal_places=2, default=0.25, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='leaf_hop_trub',
|
||||
field=models.DecimalField(decimal_places=10, default=0.0083454045, help_text='liters/gram', max_digits=12),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='mash_ratio',
|
||||
field=models.DecimalField(decimal_places=2, default=2.6, help_text='liters/kilogram', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='mt_capacity',
|
||||
field=models.DecimalField(decimal_places=2, default=10, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='mt_deadspace',
|
||||
field=models.DecimalField(decimal_places=2, default=0.25, help_text='liters', max_digits=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentprofile',
|
||||
name='pellet_hop_trub',
|
||||
field=models.DecimalField(decimal_places=10, default=0.003338162, help_text='liters/gram', max_digits=12),
|
||||
),
|
||||
]
|
@ -14,7 +14,10 @@ logger = logging.getLogger('django')
|
||||
|
||||
class CustomModel(models.Model):
|
||||
""" Custom model class with default fields to use. """
|
||||
created_date = models.DateTimeField(default=timezone.now)
|
||||
created_date = models.DateTimeField(
|
||||
default=timezone.now,
|
||||
editable=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@ -41,13 +44,13 @@ class Batch(CustomModel):
|
||||
# Brewday Measurements
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
## Mash
|
||||
# Mash
|
||||
first_runnings = models.DecimalField(
|
||||
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||
mash_ph = models.DecimalField(
|
||||
max_digits=4, decimal_places=3, null=True, blank=True)
|
||||
|
||||
## Boil
|
||||
# Boil
|
||||
pre_boil_vol = models.DecimalField(
|
||||
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||
pre_boil_sg = models.DecimalField(
|
||||
@ -57,7 +60,7 @@ class Batch(CustomModel):
|
||||
post_boil_sg = models.DecimalField(
|
||||
max_digits=5, decimal_places=4, null=True, blank=True)
|
||||
|
||||
## Ferment
|
||||
# Ferment
|
||||
fermenter_topup_vol = models.DecimalField(
|
||||
max_digits=8, decimal_places=4, null=True, blank=True)
|
||||
fermenter_vol = models.DecimalField(
|
||||
@ -67,11 +70,11 @@ class Batch(CustomModel):
|
||||
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:
|
||||
# braukaiser.com/wiki/index.php/Troubleshooting_Brewhouse_Efficiency
|
||||
# - Mash Efficiency
|
||||
# - ABV
|
||||
# - Attenuation
|
||||
# - Actual Boil-Off Rate
|
||||
# - Actual Trub/Chiller Loss
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@ -80,7 +83,7 @@ class Batch(CustomModel):
|
||||
@property
|
||||
def brewhouse_efficiency(self):
|
||||
try:
|
||||
return round(self.boil_extract_kg/self.recipe.total_extract_kg,4)
|
||||
return round(self.boil_extract_kg/self.recipe.total_extract_kg, 4)
|
||||
except ZeroDivisionError:
|
||||
return 0
|
||||
|
||||
@ -91,17 +94,23 @@ class Batch(CustomModel):
|
||||
@property
|
||||
def conversion_efficiency(self):
|
||||
""" Calculate conversion efficiency of mash."""
|
||||
if self.first_runnings is None or self.recipe.fermentable_weight_kg == 0:
|
||||
if (self.first_runnings is None
|
||||
or self.recipe.fermentable_weight_kg == 0):
|
||||
return '-'
|
||||
|
||||
return round((sg_plato(self.first_runnings)/self.recipe.fw_max)
|
||||
* (100-self.recipe.fw_max) / (100-sg_plato(self.first_runnings))
|
||||
, 4)
|
||||
* (100-self.recipe.fw_max)
|
||||
/ (100-sg_plato(self.first_runnings)), 4)
|
||||
|
||||
@property
|
||||
def boil_off_calcualted(self):
|
||||
return float(self.pre_boil_vol - self.post_boil_vol) * .96
|
||||
|
||||
@property
|
||||
def trub_loss_calculated(self):
|
||||
transfered_volume = self.fermenter_vol - self.fermenter_topup_vol
|
||||
return self.post_boil_vol-transfered_volume
|
||||
|
||||
@property
|
||||
def brewfather_url(self):
|
||||
return '{}/tabs/batches/batch/{}'.format(
|
||||
@ -144,7 +153,10 @@ class Supplier(CustomModel):
|
||||
|
||||
class CustomIngredient(CustomModel):
|
||||
""" Custom model class with default fields to use. """
|
||||
created_date = models.DateTimeField(default=timezone.now)
|
||||
created_date = models.DateTimeField(
|
||||
default=timezone.now,
|
||||
editable=False
|
||||
)
|
||||
name = models.CharField(max_length=50)
|
||||
units = models.ForeignKey(Unit, on_delete=models.PROTECT)
|
||||
unit_cost = models.DecimalField(
|
||||
@ -386,7 +398,8 @@ class Fermentable(CustomIngredient):
|
||||
|
||||
@property
|
||||
def extract_percent(self):
|
||||
return ((float(self.potential)-1)*1000/46.17) * (1-float(self.moisture)/100)
|
||||
return (((float(self.potential)-1)*1000/46.17)
|
||||
* (1-float(self.moisture)/100))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -479,7 +492,8 @@ class RecipeHop(CustomModel):
|
||||
+ (self.recipe.original_sg-1)) / 2)
|
||||
|
||||
if self.use == 1:
|
||||
conc = (float((self.hop.alpha/100)) * float(self.quantity)*0.0352739619
|
||||
conc = (float(self.hop.alpha/100)
|
||||
* convert(self.quantity, 'g', 'oz')
|
||||
* 7490/convert(self.recipe.final_volume, 'l', 'gal'))
|
||||
util = (hop_bonus*1.65*0.000125**average_wort_sg
|
||||
* ((1-2.71828182845904**(-0.04*self.time)) / 4.15))
|
||||
@ -575,31 +589,39 @@ class EquipmentProfile(CustomModel):
|
||||
|
||||
# Water managment stuff
|
||||
hlt_deadspace = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.25)
|
||||
max_digits=6, decimal_places=2, default=0.25, help_text='liters')
|
||||
mt_deadspace = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.25)
|
||||
max_digits=6, decimal_places=2, default=0.25, help_text='liters')
|
||||
mt_capacity = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=10)
|
||||
max_digits=6, decimal_places=2, default=10, help_text='liters')
|
||||
grain_absorption = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.12) # gal/lb
|
||||
max_digits=6, decimal_places=2,
|
||||
default=0.12, help_text='liters/kilogram')
|
||||
kettle_deadspace = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.25)
|
||||
max_digits=6, decimal_places=2, default=0.25, help_text='liters')
|
||||
kettle_plumbing_loss = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.25)
|
||||
max_digits=6, decimal_places=2, default=0.25, help_text='liters')
|
||||
kettle_boil_rate = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=0.5) # gal/hr
|
||||
max_digits=6, decimal_places=2,
|
||||
default=0.5, help_text='liters/hr')
|
||||
batch_volume = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=5.5)
|
||||
max_digits=6, decimal_places=2, default=5.5, help_text='liters')
|
||||
leaf_hop_trub = models.DecimalField(
|
||||
max_digits=6, decimal_places=4, default=0.0625)
|
||||
max_digits=12, decimal_places=10,
|
||||
default=0.0083454045, help_text='liters/gram')
|
||||
pellet_hop_trub = models.DecimalField(
|
||||
max_digits=6, decimal_places=4, default=0.025)
|
||||
max_digits=12, decimal_places=10,
|
||||
default=0.0033381620, help_text='liters/gram')
|
||||
hops_remain_kettle = models.BooleanField(default=True)
|
||||
mash_ratio = models.DecimalField(
|
||||
max_digits=6, decimal_places=2, default=2.6) # 1.25 qt/lb
|
||||
max_digits=6, decimal_places=2,
|
||||
default=2.6, help_text='liters/kilogram')
|
||||
|
||||
# Thermal Properties
|
||||
mt_initial_hear = models.DecimalField(
|
||||
max_digits=6, decimal_places=4, default=0.74)
|
||||
mt_heat_loss_hour = models.DecimalField(
|
||||
max_digits=6, decimal_places=4, default=2.0)
|
||||
|
||||
class Meta:
|
||||
db_table_comment = 'Volumes in liters and weights in kg.'
|
||||
|
Loading…
Reference in New Issue
Block a user