Compare commits

...

4 Commits

Author SHA1 Message Date
b39bfdd267 Add BJCP guidelines to the database. 2024-06-27 09:56:09 -04:00
53b7a1b128 Just PEP8 stuff. 2024-06-26 14:56:29 -04:00
e8c9196fc0 Field help text.
Specify units used.
2024-06-26 14:56:09 -04:00
93b93a1448 make timestamp non-editable.
Hides from admin panel since it's generated automatically.
2024-06-26 14:53:35 -04:00
15 changed files with 12974 additions and 14877 deletions

View File

@ -7,7 +7,6 @@ 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):

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.0.6 on 2024-06-26 19:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0017_alter_equipmentprofile_batch_volume_and_more'),
]
operations = [
migrations.AddField(
model_name='recipe',
name='bjcp_category_id',
field=models.CharField(default='1', max_length=3),
),
migrations.AddField(
model_name='recipe',
name='bjcp_style_id',
field=models.CharField(default='1A', max_length=3),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2024-06-26 19:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0018_recipe_bjcp_category_id_recipe_bjcp_style_id'),
]
operations = [
migrations.AlterField(
model_name='recipe',
name='bjcp_style_id',
field=models.CharField(choices=[('1A', 'American Light Lager'), ('1B', 'American Lager'), ('1C', 'Cream Ale'), ('1D', 'American Wheat Beer'), ('2A', 'International Pale Lager'), ('2B', 'International Amber Lager'), ('2C', 'International Dark Lager'), ('3A', 'Czech Pale Lager'), ('3B', 'Czech Premium Pale Lager'), ('3C', 'Czech Amber Lager'), ('3D', 'Czech Dark Lager'), ('4A', 'Munich Helles'), ('4B', 'Festbier'), ('4C', 'Helles Bock'), ('5A', 'German Leichtbier'), ('5B', 'Kölsch'), ('5C', 'German Helles Exportbier'), ('5D', 'German Pils'), ('6A', 'Märzen'), ('6B', 'Rauchbier'), ('6C', 'Dunkles Bock'), ('7A', 'Vienna Lager'), ('7B', 'Altbier'), ('8A', 'Munich Dunkel'), ('8B', 'Schwarzbier'), ('9A', 'Doppelbock'), ('9B', 'Eisbock'), ('9C', 'Baltic Porter'), ('10A', 'Weissbier'), ('10B', 'Dunkles Weissbier'), ('10C', 'Weizenbock'), ('11A', 'Ordinary Bitter'), ('11B', 'Best Bitter'), ('11C', 'Strong Bitter'), ('12A', 'British Golden Ale'), ('12B', 'Australian Sparkling Ale'), ('12C', 'English IPA'), ('13A', 'Dark Mild'), ('13B', 'British Brown Ale'), ('13C', 'English Porter'), ('14A', 'Scottish Light'), ('14B', 'Scottish Heavy'), ('14C', 'Scottish Export'), ('15A', 'Irish Red Ale'), ('15B', 'Irish Stout'), ('15C', 'Irish Extra Stout'), ('16A', 'Sweet Stout'), ('16B', 'Oatmeal Stout'), ('16C', 'Tropical Stout'), ('16D', 'Foreign Extra Stout'), ('17A', 'British Strong Ale'), ('17B', 'Old Ale'), ('17C', 'Wee Heavy'), ('17D', 'English Barley Wine'), ('18A', 'Blonde Ale'), ('18B', 'American Pale Ale'), ('19A', 'American Amber Ale'), ('19B', 'California Common'), ('19C', 'American Brown Ale'), ('20A', 'American Porter'), ('20B', 'American Stout'), ('20C', 'Imperial Stout'), ('21A', 'American IPA'), ('21B', 'Specialty IPA'), ('21C', 'Hazy IPA'), ('22A', 'Double IPA'), ('22B', 'American Strong Ale'), ('22C', 'American Barleywine'), ('22D', 'Wheatwine'), ('23A', 'Berliner Weisse'), ('23B', 'Flanders Red Ale'), ('23C', 'Oud Bruin'), ('23D', 'Lambic'), ('23E', 'Gueuze'), ('23F', 'Fruit Lambic'), ('23G', 'Gose'), ('24A', 'Witbier'), ('24B', 'Belgian Pale Ale'), ('24C', 'Bière de Garde'), ('25A', 'Belgian Blond Ale'), ('25B', 'Saison'), ('25C', 'Belgian Golden Strong Ale'), ('26A', 'Belgian Single'), ('26B', 'Belgian Dubbel'), ('26C', 'Belgian Tripel'), ('26D', 'Belgian Dark Strong Ale'), ('28A', 'Brett Beer'), ('28B', 'Mixed-Fermentation Sour Beer'), ('28C', 'Wild Specialty Beer'), ('28D', 'Straight Sour Beer'), ('29A', 'Fruit Beer'), ('29B', 'Fruit and Spice Beer'), ('29C', 'Specialty Fruit Beer'), ('29D', 'Grape Ale'), ('30A', 'Spice, Herb, or Vegetable Beer'), ('30B', 'Autumn Seasonal Beer'), ('30C', 'Winter Seasonal Beer'), ('30D', 'Specialty Spice Beer'), ('31A', 'Alternative Grain Beer'), ('31B', 'Alternative Sugar Beer'), ('32A', 'Classic Style Smoked Beer'), ('32B', 'Specialty Smoked Beer'), ('34A', 'Commercial Specialty Beer'), ('34B', 'Mixed-Style Beer'), ('34C', 'Experimental Beer'), ('X1', 'Dorada Pampeana'), ('X2', 'IPA Argenta'), ('X3', 'Italian Grape Ale'), ('X4', 'Catharina Sour'), ('X5', 'New Zealand Pilsner')], default='1A', max_length=3),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2024-06-26 19:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0019_alter_recipe_bjcp_style_id'),
]
operations = [
migrations.AlterField(
model_name='recipe',
name='bjcp_style_id',
field=models.CharField(choices=[('1A', '1A: American Light Lager'), ('1B', '1B: American Lager'), ('1C', '1C: Cream Ale'), ('1D', '1D: American Wheat Beer'), ('2A', '2A: International Pale Lager'), ('2B', '2B: International Amber Lager'), ('2C', '2C: International Dark Lager'), ('3A', '3A: Czech Pale Lager'), ('3B', '3B: Czech Premium Pale Lager'), ('3C', '3C: Czech Amber Lager'), ('3D', '3D: Czech Dark Lager'), ('4A', '4A: Munich Helles'), ('4B', '4B: Festbier'), ('4C', '4C: Helles Bock'), ('5A', '5A: German Leichtbier'), ('5B', '5B: Kölsch'), ('5C', '5C: German Helles Exportbier'), ('5D', '5D: German Pils'), ('6A', '6A: Märzen'), ('6B', '6B: Rauchbier'), ('6C', '6C: Dunkles Bock'), ('7A', '7A: Vienna Lager'), ('7B', '7B: Altbier'), ('8A', '8A: Munich Dunkel'), ('8B', '8B: Schwarzbier'), ('9A', '9A: Doppelbock'), ('9B', '9B: Eisbock'), ('9C', '9C: Baltic Porter'), ('10A', '10A: Weissbier'), ('10B', '10B: Dunkles Weissbier'), ('10C', '10C: Weizenbock'), ('11A', '11A: Ordinary Bitter'), ('11B', '11B: Best Bitter'), ('11C', '11C: Strong Bitter'), ('12A', '12A: British Golden Ale'), ('12B', '12B: Australian Sparkling Ale'), ('12C', '12C: English IPA'), ('13A', '13A: Dark Mild'), ('13B', '13B: British Brown Ale'), ('13C', '13C: English Porter'), ('14A', '14A: Scottish Light'), ('14B', '14B: Scottish Heavy'), ('14C', '14C: Scottish Export'), ('15A', '15A: Irish Red Ale'), ('15B', '15B: Irish Stout'), ('15C', '15C: Irish Extra Stout'), ('16A', '16A: Sweet Stout'), ('16B', '16B: Oatmeal Stout'), ('16C', '16C: Tropical Stout'), ('16D', '16D: Foreign Extra Stout'), ('17A', '17A: British Strong Ale'), ('17B', '17B: Old Ale'), ('17C', '17C: Wee Heavy'), ('17D', '17D: English Barley Wine'), ('18A', '18A: Blonde Ale'), ('18B', '18B: American Pale Ale'), ('19A', '19A: American Amber Ale'), ('19B', '19B: California Common'), ('19C', '19C: American Brown Ale'), ('20A', '20A: American Porter'), ('20B', '20B: American Stout'), ('20C', '20C: Imperial Stout'), ('21A', '21A: American IPA'), ('21B', '21B: Specialty IPA'), ('21C', '21C: Hazy IPA'), ('22A', '22A: Double IPA'), ('22B', '22B: American Strong Ale'), ('22C', '22C: American Barleywine'), ('22D', '22D: Wheatwine'), ('23A', '23A: Berliner Weisse'), ('23B', '23B: Flanders Red Ale'), ('23C', '23C: Oud Bruin'), ('23D', '23D: Lambic'), ('23E', '23E: Gueuze'), ('23F', '23F: Fruit Lambic'), ('23G', '23G: Gose'), ('24A', '24A: Witbier'), ('24B', '24B: Belgian Pale Ale'), ('24C', '24C: Bière de Garde'), ('25A', '25A: Belgian Blond Ale'), ('25B', '25B: Saison'), ('25C', '25C: Belgian Golden Strong Ale'), ('26A', '26A: Belgian Single'), ('26B', '26B: Belgian Dubbel'), ('26C', '26C: Belgian Tripel'), ('26D', '26D: Belgian Dark Strong Ale'), ('28A', '28A: Brett Beer'), ('28B', '28B: Mixed-Fermentation Sour Beer'), ('28C', '28C: Wild Specialty Beer'), ('28D', '28D: Straight Sour Beer'), ('29A', '29A: Fruit Beer'), ('29B', '29B: Fruit and Spice Beer'), ('29C', '29C: Specialty Fruit Beer'), ('29D', '29D: Grape Ale'), ('30A', '30A: Spice, Herb, or Vegetable Beer'), ('30B', '30B: Autumn Seasonal Beer'), ('30C', '30C: Winter Seasonal Beer'), ('30D', '30D: Specialty Spice Beer'), ('31A', '31A: Alternative Grain Beer'), ('31B', '31B: Alternative Sugar Beer'), ('32A', '32A: Classic Style Smoked Beer'), ('32B', '32B: Specialty Smoked Beer'), ('34A', '34A: Commercial Specialty Beer'), ('34B', '34B: Mixed-Style Beer'), ('34C', '34C: Experimental Beer'), ('X1', 'X1: Dorada Pampeana'), ('X2', 'X2: IPA Argenta'), ('X3', 'X3: Italian Grape Ale'), ('X4', 'X4: Catharina Sour'), ('X5', 'X5: New Zealand Pilsner')], default='1A', max_length=3),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 5.0.6 on 2024-06-26 19:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('beer', '0020_alter_recipe_bjcp_style_id'),
]
operations = [
migrations.RemoveField(
model_name='recipe',
name='bjcp_category_id',
),
]

View File

@ -0,0 +1,72 @@
# Generated by Django 5.0.6 on 2024-06-27 13:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0021_remove_recipe_bjcp_category_id'),
]
operations = [
migrations.CreateModel(
name='BjcpStyle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, null=True)),
('category', models.TextField(blank=True, null=True)),
('category_id', models.TextField(blank=True, null=True)),
('style_id', models.TextField(blank=True, null=True)),
('category_description', models.TextField(blank=True, null=True)),
('overall_impression', models.TextField(blank=True, null=True)),
('aroma', models.TextField(blank=True, null=True)),
('appearance', models.TextField(blank=True, null=True)),
('flavor', models.TextField(blank=True, null=True)),
('mouthfeel', models.TextField(blank=True, null=True)),
('comments', models.TextField(blank=True, null=True)),
('history', models.TextField(blank=True, null=True)),
('style_comparison', models.TextField(blank=True, null=True)),
('tags', models.TextField(blank=True, null=True)),
('original_gravity_minimum_unit', models.TextField(blank=True, null=True)),
('original_gravity_minimum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('original_gravity_maximum_unit', models.TextField(blank=True, null=True)),
('original_gravity_maximum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('international_bitterness_units_minimum_unit', models.TextField(blank=True, null=True)),
('international_bitterness_units_minimum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('international_bitterness_units_maximum_unit', models.TextField(blank=True, null=True)),
('international_bitterness_units_maximum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('final_gravity_minimum_unit', models.TextField(blank=True, null=True)),
('final_gravity_minimum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('final_gravity_maximum_unit', models.TextField(blank=True, null=True)),
('final_gravity_maximum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('alcohol_by_volume_minimum_unit', models.TextField(blank=True, null=True)),
('alcohol_by_volume_minimum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('alcohol_by_volume_maximum_unit', models.TextField(blank=True, null=True)),
('alcohol_by_volume_maximum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('color_minimum_unit', models.TextField(blank=True, null=True)),
('color_minimum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('color_maximum_unit', models.TextField(blank=True, null=True)),
('color_maximum_value', models.DecimalField(blank=True, decimal_places=5, max_digits=10, null=True)),
('ingredients', models.TextField(blank=True, null=True)),
('examples', models.TextField(blank=True, null=True)),
('style_guide', models.TextField(blank=True, null=True)),
('type', models.TextField(blank=True, null=True)),
('entry_instructions', models.TextField(blank=True, null=True)),
('notes', models.TextField(blank=True, null=True)),
('currently_defined_types', models.TextField(blank=True, null=True)),
('strength_classifications', models.TextField(blank=True, null=True)),
('vital_statistics', models.TextField(blank=True, null=True)),
('profile', models.TextField(blank=True, null=True)),
('comparison', models.TextField(blank=True, null=True)),
],
options={
'db_table': 'beer_bjcp',
},
),
migrations.AlterField(
model_name='recipe',
name='bjcp_style_id',
field=models.CharField(choices=[('1A', '1A: American Light Lager'), ('1B', '1B: American Lager'), ('1C', '1C: Cream Ale'), ('1D', '1D: American Wheat Beer'), ('2A', '2A: International Pale Lager'), ('2B', '2B: International Amber Lager'), ('2C', '2C: International Dark Lager'), ('3A', '3A: Czech Pale Lager'), ('3B', '3B: Czech Premium Pale Lager'), ('3C', '3C: Czech Amber Lager'), ('3D', '3D: Czech Dark Lager'), ('4A', '4A: Munich Helles'), ('4B', '4B: Festbier'), ('4C', '4C: Helles Bock'), ('5A', '5A: German Leichtbier'), ('5B', '5B: Kölsch'), ('5C', '5C: German Helles Exportbier'), ('5D', '5D: German Pils'), ('6A', '6A: Märzen'), ('6B', '6B: Rauchbier'), ('6C', '6C: Dunkles Bock'), ('7A', '7A: Vienna Lager'), ('7B', '7B: Altbier'), ('8A', '8A: Munich Dunkel'), ('8B', '8B: Schwarzbier'), ('9A', '9A: Doppelbock'), ('9B', '9B: Eisbock'), ('9C', '9C: Baltic Porter'), ('10A', '10A: Weissbier'), ('10B', '10B: Dunkles Weissbier'), ('10C', '10C: Weizenbock'), ('11A', '11A: Ordinary Bitter'), ('11B', '11B: Best Bitter'), ('11C', '11C: Strong Bitter'), ('12A', '12A: British Golden Ale'), ('12B', '12B: Australian Sparkling Ale'), ('12C', '12C: English IPA'), ('13A', '13A: Dark Mild'), ('13B', '13B: British Brown Ale'), ('13C', '13C: English Porter'), ('14A', '14A: Scottish Light'), ('14B', '14B: Scottish Heavy'), ('14C', '14C: Scottish Export'), ('15A', '15A: Irish Red Ale'), ('15B', '15B: Irish Stout'), ('15C', '15C: Irish Extra Stout'), ('16A', '16A: Sweet Stout'), ('16B', '16B: Oatmeal Stout'), ('16C', '16C: Tropical Stout'), ('16D', '16D: Foreign Extra Stout'), ('17A', '17A: British Strong Ale'), ('17B', '17B: Old Ale'), ('17C', '17C: Wee Heavy'), ('17D', '17D: English Barley Wine'), ('18A', '18A: Blonde Ale'), ('18B', '18B: American Pale Ale'), ('19A', '19A: American Amber Ale'), ('19B', '19B: California Common'), ('19C', '19C: American Brown Ale'), ('20A', '20A: American Porter'), ('20B', '20B: American Stout'), ('20C', '20C: Imperial Stout'), ('21A', '21A: American IPA'), ('21B', '21B: Specialty IPA'), ('21C', '21C: Hazy IPA'), ('22A', '22A: Double IPA'), ('22B', '22B: American Strong Ale'), ('22C', '22C: American Barleywine'), ('22D', '22D: Wheatwine'), ('23A', '23A: Berliner Weisse'), ('23B', '23B: Flanders Red Ale'), ('23C', '23C: Oud Bruin'), ('23D', '23D: Lambic'), ('23E', '23E: Gueuze'), ('23F', '23F: Fruit Lambic'), ('23G', '23G: Gose'), ('24A', '24A: Witbier'), ('24B', '24B: Belgian Pale Ale'), ('24C', '24C: Bière de Garde'), ('25A', '25A: Belgian Blond Ale'), ('25B', '25B: Saison'), ('25C', '25C: Belgian Golden Strong Ale'), ('26A', '26A: Belgian Single'), ('26B', '26B: Belgian Dubbel'), ('26C', '26C: Belgian Tripel'), ('26D', '26D: Belgian Dark Strong Ale'), ('28A', '28A: Brett Beer'), ('28B', '28B: Mixed-Fermentation Sour Beer'), ('28C', '28C: Wild Specialty Beer'), ('28D', '28D: Straight Sour Beer'), ('29A', '29A: Fruit Beer'), ('29B', '29B: Fruit and Spice Beer'), ('29C', '29C: Specialty Fruit Beer'), ('29D', '29D: Grape Ale'), ('30A', '30A: Spice, Herb, or Vegetable Beer'), ('30B', '30B: Autumn Seasonal Beer'), ('30C', '30C: Winter Seasonal Beer'), ('30D', '30D: Specialty Spice Beer'), ('31A', '31A: Alternative Grain Beer'), ('31B', '31B: Alternative Sugar Beer'), ('32A', '32A: Classic Style Smoked Beer'), ('32B', '32B: Specialty Smoked Beer'), ('34A', '34A: Commercial Specialty Beer'), ('34B', '34B: Mixed-Style Beer'), ('34C', '34C: Experimental Beer'), ('X5', 'X5: New Zealand Pilsner')], default='1A', max_length=3),
),
]

View File

@ -0,0 +1,88 @@
# Generated by Django 5.0.6 on 2024-06-27 13:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0022_bjcpstyle_alter_recipe_bjcp_style_id'),
]
operations = [
migrations.AlterField(
model_name='bjcpstyle',
name='alcohol_by_volume_maximum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='alcohol_by_volume_minimum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='category',
field=models.TextField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='category_id',
field=models.TextField(blank=True, max_length=5, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='color_maximum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='color_minimum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='final_gravity_maximum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='final_gravity_minimum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='international_bitterness_units_maximum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='international_bitterness_units_minimum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='name',
field=models.TextField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='original_gravity_maximum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='original_gravity_minimum_unit',
field=models.TextField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='style_guide',
field=models.TextField(blank=True, max_length=20, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='style_id',
field=models.TextField(blank=True, max_length=5, null=True),
),
]

View File

@ -0,0 +1,88 @@
# Generated by Django 5.0.6 on 2024-06-27 13:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('beer', '0023_alter_bjcpstyle_alcohol_by_volume_maximum_unit_and_more'),
]
operations = [
migrations.AlterField(
model_name='bjcpstyle',
name='alcohol_by_volume_maximum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='alcohol_by_volume_minimum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='category',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='category_id',
field=models.CharField(blank=True, max_length=5, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='color_maximum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='color_minimum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='final_gravity_maximum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='final_gravity_minimum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='international_bitterness_units_maximum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='international_bitterness_units_minimum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='name',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='original_gravity_maximum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='original_gravity_minimum_unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='style_guide',
field=models.CharField(blank=True, max_length=20, null=True),
),
migrations.AlterField(
model_name='bjcpstyle',
name='style_id',
field=models.CharField(blank=True, max_length=5, null=True),
),
]

View File

@ -1,5 +1,6 @@
from django.db import models from django.db import models
from django.db.models import Sum, Q from django.db.models import Sum, Q
from django.contrib.staticfiles import finders
from django.utils import timezone from django.utils import timezone
from django_cryptography.fields import encrypt from django_cryptography.fields import encrypt
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
@ -8,13 +9,17 @@ from config.extras import BREWFATHER_APP_ROOT
from beer.extras import sg_plato, plato_sg, kg_extract, convert from beer.extras import sg_plato, plato_sg, kg_extract, convert
from django.conf import settings from django.conf import settings
import json
import logging import logging
logger = logging.getLogger('django') logger = logging.getLogger('django')
class CustomModel(models.Model): class CustomModel(models.Model):
""" Custom model class with default fields to use. """ """ 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: class Meta:
abstract = True abstract = True
@ -41,13 +46,13 @@ class Batch(CustomModel):
# Brewday Measurements # Brewday Measurements
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
## Mash # 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( mash_ph = models.DecimalField(
max_digits=4, decimal_places=3, null=True, blank=True) max_digits=4, decimal_places=3, null=True, blank=True)
## Boil # Boil
pre_boil_vol = models.DecimalField( pre_boil_vol = models.DecimalField(
max_digits=8, decimal_places=4, null=True, blank=True) max_digits=8, decimal_places=4, null=True, blank=True)
pre_boil_sg = models.DecimalField( pre_boil_sg = models.DecimalField(
@ -57,7 +62,7 @@ class Batch(CustomModel):
post_boil_sg = models.DecimalField( post_boil_sg = models.DecimalField(
max_digits=5, decimal_places=4, null=True, blank=True) max_digits=5, decimal_places=4, null=True, blank=True)
## Ferment # Ferment
fermenter_topup_vol = models.DecimalField( fermenter_topup_vol = models.DecimalField(
max_digits=8, decimal_places=4, null=True, blank=True) max_digits=8, decimal_places=4, null=True, blank=True)
fermenter_vol = models.DecimalField( fermenter_vol = models.DecimalField(
@ -67,11 +72,11 @@ class Batch(CustomModel):
final_sg = models.DecimalField( final_sg = models.DecimalField(
max_digits=5, decimal_places=4, null=True, blank=True) 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 # - Mash Efficiency
# - ABV # - ABV
# - Attenuation # - Attenuation
# - Actual Boil-Off Rate
# - Actual Trub/Chiller Loss # - Actual Trub/Chiller Loss
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@ -80,7 +85,7 @@ class Batch(CustomModel):
@property @property
def brewhouse_efficiency(self): def brewhouse_efficiency(self):
try: 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: except ZeroDivisionError:
return 0 return 0
@ -91,17 +96,23 @@ class Batch(CustomModel):
@property @property
def conversion_efficiency(self): def conversion_efficiency(self):
""" Calculate conversion efficiency of mash.""" """ 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 '-'
return round((sg_plato(self.first_runnings)/self.recipe.fw_max) return round((sg_plato(self.first_runnings)/self.recipe.fw_max)
* (100-self.recipe.fw_max) / (100-sg_plato(self.first_runnings)) * (100-self.recipe.fw_max)
, 4) / (100-sg_plato(self.first_runnings)), 4)
@property @property
def boil_off_calcualted(self): def boil_off_calcualted(self):
return float(self.pre_boil_vol - self.post_boil_vol) * .96 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 @property
def brewfather_url(self): def brewfather_url(self):
return '{}/tabs/batches/batch/{}'.format( return '{}/tabs/batches/batch/{}'.format(
@ -144,7 +155,10 @@ class Supplier(CustomModel):
class CustomIngredient(CustomModel): class CustomIngredient(CustomModel):
""" Custom model class with default fields to use. """ """ 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) name = models.CharField(max_length=50)
units = models.ForeignKey(Unit, on_delete=models.PROTECT) units = models.ForeignKey(Unit, on_delete=models.PROTECT)
unit_cost = models.DecimalField( unit_cost = models.DecimalField(
@ -176,10 +190,25 @@ class Recipe(CustomModel):
fermentables = models.ManyToManyField( fermentables = models.ManyToManyField(
'Fermentable', through='RecipeFermentable') 'Fermentable', through='RecipeFermentable')
with open(finders.find('bjcp/2021.json'), encoding='utf-8', errors="ignore") as bjcp_file:
bjcp = json.load(bjcp_file)
styles = bjcp['styles']
style_ids = dict([
(x['style_id'], '{}: {}'.format(x['style_id'], x['name']))
for x in styles
])
bjcp_style_id = models.CharField(
max_length=3, choices=style_ids, default='1A')
class Meta: class Meta:
verbose_name = 'Recipe' verbose_name = 'Recipe'
verbose_name_plural = 'Recipes' verbose_name_plural = 'Recipes'
@property
def batch_size_display(self):
return convert(self.batch_size, 'l', 'gal')
@property @property
def fw_max(self): def fw_max(self):
potential = 0 potential = 0
@ -193,6 +222,10 @@ class Recipe(CustomModel):
ratio = float(self.equipment.mash_ratio) ratio = float(self.equipment.mash_ratio)
return 100*e_grain / (ratio+e_grain) return 100*e_grain / (ratio+e_grain)
@property
def total_fermentable_display(self):
return convert(self.fermentable_weight_kg, 'kg', 'lb')
@property @property
def fermentable_weight_kg(self): def fermentable_weight_kg(self):
"""Weight of all fermentables attached to recipe.""" """Weight of all fermentables attached to recipe."""
@ -212,10 +245,18 @@ class Recipe(CustomModel):
return extract return extract
@property @property
def hop_weight(self): def total_hop_display(self):
return convert(self.hop_weight_g, 'g', 'oz')
@property
def hop_weight_g(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 float(aggregate['quantity__sum']) * 0.0352739619
if aggregate['quantity__sum']:
return aggregate['quantity__sum']
else:
return 0
@property @property
def final_volume(self): def final_volume(self):
@ -386,7 +427,8 @@ class Fermentable(CustomIngredient):
@property @property
def extract_percent(self): 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): def __str__(self):
return self.name return self.name
@ -479,7 +521,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)) * float(self.quantity)*0.0352739619 conc = (float(self.hop.alpha/100)
* convert(self.quantity, 'g', 'oz')
* 7490/convert(self.recipe.final_volume, 'l', 'gal')) * 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))
@ -575,31 +618,96 @@ class EquipmentProfile(CustomModel):
# Water managment stuff # Water managment stuff
hlt_deadspace = models.DecimalField( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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) hops_remain_kettle = models.BooleanField(default=True)
mash_ratio = models.DecimalField( 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 # Thermal Properties
mt_initial_hear = models.DecimalField( mt_initial_hear = models.DecimalField(
max_digits=6, decimal_places=4, default=0.74) max_digits=6, decimal_places=4, default=0.74)
mt_heat_loss_hour = models.DecimalField( mt_heat_loss_hour = models.DecimalField(
max_digits=6, decimal_places=4, default=2.0) max_digits=6, decimal_places=4, default=2.0)
class Meta:
db_table_comment = 'Volumes in liters and weights in kg.'
class BjcpStyle(models.Model):
name = models.CharField(max_length=50, blank=True, null=True)
category = models.CharField(max_length=50, blank=True, null=True)
category_id = models.CharField(max_length=5, blank=True, null=True)
style_id = models.CharField(max_length=5, blank=True, null=True)
category_description = models.TextField(blank=True, null=True)
overall_impression = models.TextField(blank=True, null=True)
aroma = models.TextField(blank=True, null=True)
appearance = models.TextField(blank=True, null=True)
flavor = models.TextField(blank=True, null=True)
mouthfeel = models.TextField(blank=True, null=True)
comments = models.TextField(blank=True, null=True)
history = models.TextField(blank=True, null=True)
style_comparison = models.TextField(blank=True, null=True)
tags = models.TextField(blank=True, null=True)
original_gravity_minimum_unit = models.CharField(max_length=10, blank=True, null=True)
original_gravity_minimum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
original_gravity_maximum_unit = models.CharField(max_length=10, blank=True, null=True)
original_gravity_maximum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
international_bitterness_units_minimum_unit = models.CharField(max_length=10, blank=True, null=True)
international_bitterness_units_minimum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
international_bitterness_units_maximum_unit = models.CharField(max_length=10, blank=True, null=True)
international_bitterness_units_maximum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
final_gravity_minimum_unit = models.CharField(max_length=10, blank=True, null=True)
final_gravity_minimum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
final_gravity_maximum_unit = models.CharField(max_length=10, blank=True, null=True)
final_gravity_maximum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
alcohol_by_volume_minimum_unit = models.CharField(max_length=10, blank=True, null=True)
alcohol_by_volume_minimum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
alcohol_by_volume_maximum_unit = models.CharField(max_length=10, blank=True, null=True)
alcohol_by_volume_maximum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
color_minimum_unit = models.CharField(max_length=10, blank=True, null=True)
color_minimum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
color_maximum_unit = models.CharField(max_length=10, blank=True, null=True)
color_maximum_value = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True) # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
ingredients = models.TextField(blank=True, null=True)
examples = models.TextField(blank=True, null=True)
style_guide = models.CharField(max_length=20, blank=True, null=True)
type = models.TextField(blank=True, null=True)
entry_instructions = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
currently_defined_types = models.TextField(blank=True, null=True)
strength_classifications = models.TextField(blank=True, null=True)
vital_statistics = models.TextField(blank=True, null=True)
profile = models.TextField(blank=True, null=True)
comparison = models.TextField(blank=True, null=True)
def __str__(self):
return '{} {}: {}'.format(
self.category,
self.style_id,
self.name
)
class Meta:
db_table = 'beer_bjcp'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
from django.urls import path, re_path from django.urls import path
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from .views import home, view_recipe, view_batch, update_ferm, update_hop from .views import home, view_recipe, view_batch, update_ferm, update_hop

12344
fixtures/bjcp.json Normal file

File diff suppressed because it is too large Load Diff