From b97982f5e0985a5dc2a5fe548b1aad4ef1899acf Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 13 Apr 2024 19:33:08 +0200 Subject: [PATCH] Fields that should always have been optional in Fermentable --- src/model/Fermentable.cpp | 30 +++++------ src/model/Fermentable.h | 68 +++++++++++++++---------- src/model/Recipe.cpp | 2 +- src/model/RecipeAdditionFermentable.cpp | 2 +- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/model/Fermentable.cpp b/src/model/Fermentable.cpp index 3e346d11..c1f0fd85 100644 --- a/src/model/Fermentable.cpp +++ b/src/model/Fermentable.cpp @@ -200,12 +200,12 @@ Fermentable::Fermentable(QString name) : m_supplier {"" }, m_notes {"" }, m_coarseFineDiff_pct {std::nullopt }, - m_moisture_pct {0.0 }, + m_moisture_pct {std::nullopt }, m_diastaticPower_lintner {std::nullopt }, - m_protein_pct {0.0 }, - m_maxInBatch_pct {100.0 }, - m_recommendMash {false }, - m_ibuGalPerLb {0.0 }, + m_protein_pct {std::nullopt }, + m_maxInBatch_pct {std::nullopt }, + m_recommendMash {std::nullopt }, + m_ibuGalPerLb {std::nullopt }, /// m_isMashed {false }, // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ m_grainGroup {std::nullopt }, @@ -322,12 +322,12 @@ QString Fermentable::origin () QString Fermentable::supplier () const { return this->m_supplier ; } QString Fermentable::notes () const { return this->m_notes ; } std::optional Fermentable::coarseFineDiff_pct () const { return this->m_coarseFineDiff_pct ; } -double Fermentable::moisture_pct () const { return this->m_moisture_pct ; } +std::optional Fermentable::moisture_pct () const { return this->m_moisture_pct ; } std::optional Fermentable::diastaticPower_lintner () const { return this->m_diastaticPower_lintner ; } -double Fermentable::protein_pct () const { return this->m_protein_pct ; } -double Fermentable::maxInBatch_pct () const { return this->m_maxInBatch_pct ; } -bool Fermentable::recommendMash () const { return this->m_recommendMash ; } -double Fermentable::ibuGalPerLb () const { return this->m_ibuGalPerLb ; } +std::optional Fermentable::protein_pct () const { return this->m_protein_pct ; } +std::optional Fermentable::maxInBatch_pct () const { return this->m_maxInBatch_pct ; } +std::optional Fermentable::recommendMash () const { return this->m_recommendMash ; } +std::optional Fermentable::ibuGalPerLb () const { return this->m_ibuGalPerLb ; } ///bool Fermentable::isMashed () const { return this->m_isMashed ; } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ std::optional Fermentable::grainGroup () const { return this->m_grainGroup ; } @@ -365,16 +365,16 @@ void Fermentable::setType (Type const void Fermentable::setOrigin (QString const & val) { SET_AND_NOTIFY(PropertyNames::Fermentable::origin , this->m_origin , val); return; } void Fermentable::setSupplier (QString const & val) { SET_AND_NOTIFY(PropertyNames::Fermentable::supplier , this->m_supplier , val); return; } void Fermentable::setNotes (QString const & val) { SET_AND_NOTIFY(PropertyNames::Fermentable::notes , this->m_notes , val); return; } -void Fermentable::setRecommendMash (bool const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::recommendMash , this->m_recommendMash , val); return; } +void Fermentable::setRecommendMash (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::recommendMash , this->m_recommendMash , val); return; } ///void Fermentable::setIsMashed (bool const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::isMashed , this->m_isMashed , val); return; } -void Fermentable::setIbuGalPerLb (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::ibuGalPerLb , this->m_ibuGalPerLb , val); return; } +void Fermentable::setIbuGalPerLb (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::ibuGalPerLb , this->m_ibuGalPerLb , val); return; } ///void Fermentable::setYield_pct (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::yield_pct , this->m_yield_pct , this->enforceMinAndMax(val, "amount", 0.0, 100.0)); return; } void Fermentable::setColor_srm (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::color_srm , this->m_color_srm , this->enforceMin (val, "color")); return; } void Fermentable::setCoarseFineDiff_pct (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::coarseFineDiff_pct , this->m_coarseFineDiff_pct , this->enforceMinAndMax(val, "coarseFineDiff", 0.0, 100.0)); return; } -void Fermentable::setMoisture_pct (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); return; } +void Fermentable::setMoisture_pct (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); return; } void Fermentable::setDiastaticPower_lintner (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::diastaticPower_lintner , this->m_diastaticPower_lintner , this->enforceMin (val, "diastatic power")); return; } -void Fermentable::setProtein_pct (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); return; } -void Fermentable::setMaxInBatch_pct (double const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); return; } +void Fermentable::setProtein_pct (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); return; } +void Fermentable::setMaxInBatch_pct (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); return; } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ void Fermentable::setGrainGroup (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , val ); return; } void Fermentable::setGrainGroupAsInt (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , Optional::fromOptInt(val)); return; } diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index 859c05d0..5e9d5e79 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -221,22 +221,31 @@ class Fermentable : public Ingredient, public IngredientBase { * .:TODO:. We should attempt to enforce this when two or more of the values are set. */ Q_PROPERTY(std::optional coarseFineDiff_pct READ coarseFineDiff_pct WRITE setCoarseFineDiff_pct ) - //! \brief The moisture in pct. - Q_PROPERTY(double moisture_pct READ moisture_pct WRITE setMoisture_pct ) - //! \brief The diastatic power in Lintner. + //! \brief The moisture in pct. Only appropriate for a "Grain" or "Other_Adjunct" type. NB Optional in both BeerXML and BeerJSON. + Q_PROPERTY(std::optional moisture_pct READ moisture_pct WRITE setMoisture_pct ) + //! \brief The diastatic power in Lintner. Only appropriate for a "Grain" or "Other_Adjunct" type. NB Optional in both BeerXML and BeerJSON. Q_PROPERTY(std::optional diastaticPower_lintner READ diastaticPower_lintner WRITE setDiastaticPower_lintner ) - //! \brief The percent protein. - Q_PROPERTY(double protein_pct READ protein_pct WRITE setProtein_pct ) - //! \brief The maximum recommended amount in a batch, as a percentage of the total grains. - Q_PROPERTY(double maxInBatch_pct READ maxInBatch_pct WRITE setMaxInBatch_pct ) + //! \brief The percent protein. Only appropriate for a "Grain" or "Other_Adjunct" type. NB Optional in both BeerXML and BeerJSON. + Q_PROPERTY(std::optional protein_pct READ protein_pct WRITE setProtein_pct ) + //! \brief The maximum recommended amount in a batch, as a percentage of the total grains. NB Optional in both BeerXML and BeerJSON. + Q_PROPERTY(std::optional maxInBatch_pct READ maxInBatch_pct WRITE setMaxInBatch_pct ) /** * \brief Whether a mash is recommended. \c true means \c Fermentable must be mashed, \c false means if it can be * steeped. Note that this does NOT indicate whether the \c Fermentable is mashed or not – it is only a - * recommendation used in recipe formulation. + * recommendation used in recipe formulation. NB Optional in both BeerXML and BeerJSON. */ - Q_PROPERTY(bool recommendMash READ recommendMash WRITE setRecommendMash ) - //! \brief The IBUs per gal/lb if this is a liquid extract. .:TODO:. Should this be a metric measure? - Q_PROPERTY(double ibuGalPerLb READ ibuGalPerLb WRITE setIbuGalPerLb ) + Q_PROPERTY(std::optional recommendMash READ recommendMash WRITE setRecommendMash ) + /** + * \brief For hopped extracts only - an estimate of the number of IBUs per pound of extract in a gallon of water. + * To convert to IBUs we multiply this number by the amount in pounds and divide by the number of gallons in + * the batch. Based on a sixty minute boil. Only suitable for use with an "Extract" type, otherwise this + * value is ignored. + * + * This is an optional field in BeerXML and unsupported in BeerJSON. + * + * .:TBD:. If we care about this, then it would be more consistent to store in internally as a metric measure. + */ + Q_PROPERTY(std::optional ibuGalPerLb READ ibuGalPerLb WRITE setIbuGalPerLb ) /// //! \brief Whether the grains actually is mashed. /// Q_PROPERTY(bool isMashed READ isMashed WRITE setIsMashed ) //! \brief Whether this fermentable is an extract. @@ -263,6 +272,11 @@ class Fermentable : public Ingredient, public IngredientBase { * We treat this as synonymous with the BeerXML field YIELD, defined as "Percent dry yield (fine grain) for * the grain, or the raw yield by weight if this is an extract adjunct or sugar." HOWEVER, note that the * BeerXML field is required whereas internally, and in BeerJSON, this is an optional field. + * + * NOTE: Actually in BeerJSON, there is a required field of yield which is a record containing optional fields + * fine_grind, coarse_grind, fine_coarse_difference and potential. Technically if none of these fields + * is set, then we should still write out an empty yield record, but we would not. Hopefully this won't + * be a problem in practice. */ Q_PROPERTY(std::optional fineGrindYield_pct READ fineGrindYield_pct WRITE setFineGrindYield_pct ) //! \brief Extract Yield Dry Basis Coarse Grind (DBCG) - aka percentage yield, compared to sucrose, of a coarse grind @@ -456,13 +470,13 @@ class Fermentable : public Ingredient, public IngredientBase { QString origin () const; QString supplier () const; QString notes () const; - std::optional coarseFineDiff_pct () const; - double moisture_pct () const; + std::optional coarseFineDiff_pct () const; + std::optional moisture_pct () const; std::optional diastaticPower_lintner () const; - double protein_pct () const; - double maxInBatch_pct () const; - bool recommendMash () const; - double ibuGalPerLb () const; + std::optional protein_pct () const; + std::optional maxInBatch_pct () const; + std::optional recommendMash () const; + std::optional ibuGalPerLb () const; /// bool isMashed () const; // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ std::optional grainGroup () const; @@ -499,12 +513,12 @@ class Fermentable : public Ingredient, public IngredientBase { void setSupplier (QString const & val); void setNotes (QString const & val); void setCoarseFineDiff_pct (std::optional const val); - void setMoisture_pct (double const val); + void setMoisture_pct (std::optional const val); void setDiastaticPower_lintner (std::optional const val); - void setProtein_pct (double const val); - void setMaxInBatch_pct (double const val); - void setRecommendMash (bool const val); - void setIbuGalPerLb (double const val); + void setProtein_pct (std::optional const val); + void setMaxInBatch_pct (std::optional const val); + void setRecommendMash (std::optional const val); + void setIbuGalPerLb (std::optional const val); /// void setIsMashed (bool const val); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ void setGrainGroup (std::optional const val); @@ -547,12 +561,12 @@ class Fermentable : public Ingredient, public IngredientBase { QString m_supplier ; QString m_notes ; std::optional m_coarseFineDiff_pct ; - double m_moisture_pct ; + std::optional m_moisture_pct ; std::optional m_diastaticPower_lintner ; - double m_protein_pct ; - double m_maxInBatch_pct ; - bool m_recommendMash ; - double m_ibuGalPerLb ; + std::optional m_protein_pct ; + std::optional m_maxInBatch_pct ; + std::optional m_recommendMash ; + std::optional m_ibuGalPerLb ; /// bool m_isMashed ; // Primarily valid in "Use Of" instance // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ std::optional m_grainGroup ; diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index 7b8de2b0..e42593d4 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -1449,7 +1449,7 @@ class Recipe::impl { for (auto const & fermentableAddition : this->m_self.fermentableAdditions()) { if (fermentableAddition->amountIsWeight()) { // Conversion factor for lb/gal to kg/l = 8.34538. - calculatedIbu += fermentableAddition->fermentable()->ibuGalPerLb() * (fermentableAddition->amount().quantity / this->m_self.batchSize_l()) / 8.34538; + calculatedIbu += fermentableAddition->fermentable()->ibuGalPerLb().value_or(0.0) * (fermentableAddition->amount().quantity / this->m_self.batchSize_l()) / 8.34538; } else { // .:TBD:. What do do about liquids qWarning() << diff --git a/src/model/RecipeAdditionFermentable.cpp b/src/model/RecipeAdditionFermentable.cpp index 7d1d9695..571b4be3 100644 --- a/src/model/RecipeAdditionFermentable.cpp +++ b/src/model/RecipeAdditionFermentable.cpp @@ -149,7 +149,7 @@ double RecipeAdditionFermentable::equivSucrose_kg() const { auto const & fermentable = this->fermentable(); double const ret = amount.quantity * fermentable->fineGrindYield_pct().value_or(0.0) * - (1.0 - fermentable->moisture_pct() / 100.0) / 100.0; + (1.0 - fermentable->moisture_pct().value_or(0.0) / 100.0) / 100.0; // If this is a steeped grain... if (fermentable->type() == Fermentable::Type::Grain && this->stage() != RecipeAddition::Stage::Mash) {