Fri, 29 Jul 2022 13:12:26 +0200
First load of changes for hops. In EditHop load the dropdown buttons from the global table. Use named query fields. Added database utilisation and bu_factor fields for hop extracts. Added edit fields for these new fields. Added post boil SG, utilisation and bu_factor parameters to the toIBU function. Added hops form parameter to the hopFlavourContribution and hopAromaContribution display bars. In the hops inventory list dispay volumes instead of weight for hop extracts. Modified the TinsethIBU function to use utilisation and bu_factor parameters. Add calculations for co2 and iso hop extracts, this is work in progress. The toIBU function makes use of the preSG and postSG values to use the correct SG to caall the TinsethIBU function. This results in a bit lower IBU values mostly affecting the late additions. Added use hop at bottling for iso hop extracts like Tetra hops using the formula from BarthHaas.
/** * EditRecipe.cpp is part of bmsapp. * * Export recipe. * * bmsapp is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bmsapp is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ void EditRecipe::exportBeerXML() { const QStringList color_method({ "Morey", "Mosher", "Daniels", "Halberstadt", "Naudts" }); const QStringList hop_forms({ "Pellet", "Plug", "Leaf", "Leaf", "Pellet", "Pellet" }); /* "Leaf Wet", "Cryo", "Extract" */ /* We use more hop forms then beerxml knows about, so we send known names */ /* instead of what we internally use. */ /* Note: Whirlpool hop is Aroma + time. Hop at flame-off is Aroma with time = 0. BeerXML is really stupid designed. */ /* flame-off vv vv whirlpool hop */ const QStringList hop_use({ "Mash", "First wort", "Boil", "Aroma", "Aroma", "Dry hop", "Dry hop" }); const QStringList yeast_type({ "Lager", "Ale", "Wheat", "Wine", "Champagne", "Other", "Other", "Other", "Other", "Other" }); const QStringList yeast_form({ "Liquid", "Dry", "Slant", "Culture", "Frozen", "Bottle", "Dry" }); QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::homePath() + "/" + recipe->name + ".xml", tr("Files (*.xml)")); if (fileName == 0) { QMessageBox::warning(this, tr("Save File"), tr("No XML file selected.")); return; } qInfo() << "Recipe to beerXML" << fileName; QFile file(fileName); file.open(QIODevice::WriteOnly); QXmlStreamWriter *xmlWriter = new QXmlStreamWriter(&file); xmlWriter->writeStartDocument(); xmlWriter->setAutoFormatting(true); xmlWriter->setAutoFormattingIndent(1); xmlWriter->writeStartElement("RECIPES"); xmlWriter->writeStartElement("RECIPE"); /* * Recipe basics */ xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->name); if (recipe->notes != "") xmlWriter->writeTextElement("NOTES", recipe->notes); xmlWriter->writeTextElement("TYPE", g_recipe_types[recipe->type]); xmlWriter->writeTextElement("BREWER", "Anonymous"); xmlWriter->writeTextElement("BATCH_SIZE", QString::number(recipe->batch_size, 'f', 4)); xmlWriter->writeTextElement("BOIL_SIZE", QString::number(recipe->boil_size, 'f', 4)); xmlWriter->writeTextElement("BOIL_TIME", QString::number(recipe->boil_time, 'f', 3)); xmlWriter->writeTextElement("EFFICIENCY", QString::number(recipe->efficiency, 'f', 4)); xmlWriter->writeTextElement("EST_OG", QString::number(recipe->est_og, 'f', 3)); xmlWriter->writeTextElement("EST_FG", QString::number(recipe->est_fg, 'f', 3)); if (recipe->est_abv > 0) xmlWriter->writeTextElement("EST_ABV", QString::number(recipe->est_abv, 'f', 1)); if (recipe->est_color > 0) { xmlWriter->writeTextElement("EST_COLOR", QString::number(Utils::ebc_to_srm(recipe->est_color), 'f', 6)); xmlWriter->writeTextElement("COLOR_METHOD", color_method[recipe->color_method]); } if (recipe->est_ibu > 0) { xmlWriter->writeTextElement("EST_IBU", QString::number(recipe->est_ibu, 'f', 1)); xmlWriter->writeTextElement("IBU_METHOD", g_ibu_method[0]); // Only Tinseth } xmlWriter->writeStartElement("STYLE"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->st_name); xmlWriter->writeTextElement("CATEGORY", recipe->st_category); xmlWriter->writeTextElement("CATEGORY_NUMBER", QString::number(recipe->st_category_number, 'f', 0)); xmlWriter->writeTextElement("STYLE_LETTER", recipe->st_letter); xmlWriter->writeTextElement("STYLE_GUIDE", recipe->st_guide); xmlWriter->writeTextElement("TYPE", g_style_types[recipe->st_type]); xmlWriter->writeTextElement("OG_MIN", QString::number(recipe->st_og_min, 'f', 3)); xmlWriter->writeTextElement("OG_MAX", QString::number(recipe->st_og_max, 'f', 3)); xmlWriter->writeTextElement("FG_MIN", QString::number(recipe->st_fg_min, 'f', 3)); xmlWriter->writeTextElement("FG_MAX", QString::number(recipe->st_fg_max, 'f', 3)); xmlWriter->writeTextElement("IBU_MIN", QString::number(recipe->st_ibu_min, 'f', 0)); xmlWriter->writeTextElement("IBU_MAX", QString::number(recipe->st_ibu_max, 'f', 0)); xmlWriter->writeTextElement("COLOR_MIN", QString::number(Utils::ebc_to_srm(recipe->st_color_min), 'f', 2)); xmlWriter->writeTextElement("COLOR_MAX", QString::number(Utils::ebc_to_srm(recipe->st_color_max), 'f', 2)); xmlWriter->writeTextElement("CARB_MIN", QString::number(recipe->st_carb_min, 'f', 1)); xmlWriter->writeTextElement("CARB_MAX", QString::number(recipe->st_carb_max, 'f', 1)); xmlWriter->writeTextElement("ABV_MIN", QString::number(recipe->st_abv_min, 'f', 1)); xmlWriter->writeTextElement("ABV_MAX", QString::number(recipe->st_abv_max, 'f', 1)); xmlWriter->writeEndElement(); // STYLE xmlWriter->writeStartElement("EQUIPMENT"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", "Dummy Brewery"); xmlWriter->writeTextElement("BATCH_SIZE", QString::number(recipe->batch_size, 'f', 2)); xmlWriter->writeTextElement("BOIL_SIZE", QString::number(recipe->boil_size, 'f', 2)); xmlWriter->writeTextElement("BOIL_TIME", QString::number(recipe->boil_time, 'f', 0)); xmlWriter->writeEndElement(); // EQUIPMENT xmlWriter->writeStartElement("HOPS"); for (int i = 0; i < recipe->hops.size(); i++) { xmlWriter->writeStartElement("HOP"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->hops.at(i).name); xmlWriter->writeTextElement("ALPHA", QString::number(recipe->hops.at(i).alpha, 'f', 1)); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->hops.at(i).amount, 'f', 4)); xmlWriter->writeTextElement("USE", hop_use[recipe->hops.at(i).useat]); xmlWriter->writeTextElement("TIME", QString::number(recipe->hops.at(i).time, 'f', 0)); xmlWriter->writeTextElement("TYPE", g_hop_types[recipe->hops.at(i).type]); xmlWriter->writeTextElement("FORM", hop_forms[recipe->hops.at(i).form]); xmlWriter->writeTextElement("BETA", QString::number(recipe->hops.at(i).beta, 'f', 1)); xmlWriter->writeTextElement("HSI", QString::number(recipe->hops.at(i).hsi, 'f', 1)); xmlWriter->writeTextElement("ORIGIN", recipe->hops.at(i).origin); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // HOPS xmlWriter->writeStartElement("FERMENTABLES"); for (int i = 0; i < recipe->fermentables.size(); i++) { xmlWriter->writeStartElement("FERMENTABLE"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->fermentables.at(i).name); xmlWriter->writeTextElement("TYPE", g_fermentable_types[recipe->fermentables.at(i).type]); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->fermentables.at(i).amount, 'f', 4)); xmlWriter->writeTextElement("YIELD", QString::number(recipe->fermentables.at(i).yield, 'f', 1)); xmlWriter->writeTextElement("COLOR", QString::number(Utils::ebc_to_srm(recipe->fermentables.at(i).color), 'f', 1)); xmlWriter->writeTextElement("ADD_AFTER_BOIL", recipe->fermentables.at(i).add_after_boil ? "TRUE":"FALSE"); xmlWriter->writeTextElement("ORIGIN", recipe->fermentables.at(i).origin); xmlWriter->writeTextElement("SUPPLIER", recipe->fermentables.at(i).supplier); if (recipe->fermentables.at(i).coarse_fine_diff) xmlWriter->writeTextElement("COARSE_FINE_DIFF", QString::number(recipe->fermentables.at(i).coarse_fine_diff, 'f', 4)); if (recipe->fermentables.at(i).moisture) xmlWriter->writeTextElement("MOISTURE", QString::number(recipe->fermentables.at(i).moisture, 'f', 4)); if (recipe->fermentables.at(i).diastatic_power) xmlWriter->writeTextElement("DIASTATIC_POWER", QString::number(recipe->fermentables.at(i).diastatic_power, 'f', 4)); if (recipe->fermentables.at(i).protein) xmlWriter->writeTextElement("PROTEIN", QString::number(recipe->fermentables.at(i).protein, 'f', 4)); if (recipe->fermentables.at(i).max_in_batch) xmlWriter->writeTextElement("MAX_IN_BATCH", QString::number(recipe->fermentables.at(i).max_in_batch, 'f', 1)); xmlWriter->writeTextElement("RECOMMEND_MASH", recipe->fermentables.at(i).recommend_mash ? "TRUE":"FALSE"); xmlWriter->writeTextElement("GRAINTYPE", g_fermentable_graintypes[recipe->fermentables.at(i).graintype]); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // FERMENTABLES xmlWriter->writeStartElement("MISCS"); for (int i = 0; i < recipe->miscs.size(); i++) { xmlWriter->writeStartElement("MISC"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->miscs.at(i).name); xmlWriter->writeTextElement("TYPE", g_misc_types[recipe->miscs.at(i).type]); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->miscs.at(i).amount, 'f', 5)); xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", recipe->miscs.at(i).amount_is_weight ? "TRUE":"FALSE"); xmlWriter->writeTextElement("USE", g_misc_uses[recipe->miscs.at(i).use_use]); xmlWriter->writeTextElement("TIME", QString::number(recipe->miscs.at(i).time, 'f', 0)); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // MISCS xmlWriter->writeStartElement("YEASTS"); for (int i = 0; i < recipe->yeasts.size(); i++) { xmlWriter->writeStartElement("YEAST"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->yeasts.at(i).name); xmlWriter->writeTextElement("TYPE", yeast_type[recipe->yeasts.at(i).type]); xmlWriter->writeTextElement("FORM", yeast_form[recipe->yeasts.at(i).form]); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->yeasts.at(i).amount, 'f', 5)); xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", (recipe->yeasts.at(i).form == 1) ? "TRUE":"FALSE"); xmlWriter->writeTextElement("LABORATORY", recipe->yeasts.at(i).laboratory); xmlWriter->writeTextElement("PRODUCT_ID", recipe->yeasts.at(i).product_id); xmlWriter->writeTextElement("MIN_TEMPERATURE", QString::number(recipe->yeasts.at(i).min_temperature, 'f', 1)); xmlWriter->writeTextElement("MAX_TEMPERATURE", QString::number(recipe->yeasts.at(i).max_temperature, 'f', 1)); xmlWriter->writeTextElement("ATTENUATION", QString::number(recipe->yeasts.at(i).attenuation, 'f', 1)); xmlWriter->writeTextElement("ADD_TO_SECONDARY", (recipe->yeasts.at(i).use == 0) ? "FALSE":"TRUE"); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // YEASTS xmlWriter->writeStartElement("WATERS"); if (recipe->w1_amount > 0) { xmlWriter->writeStartElement("WATER"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->w1_name); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->w1_amount, 'f', 2)); xmlWriter->writeTextElement("CALCIUM", QString::number(recipe->w1_calcium, 'f', 2)); xmlWriter->writeTextElement("MAGNESIUM", QString::number(recipe->w1_magnesium, 'f', 2)); xmlWriter->writeTextElement("BICARBONATE", QString::number(recipe->w1_total_alkalinity * 1.22, 'f', 2)); xmlWriter->writeTextElement("SULFATE", QString::number(recipe->w1_sulfate, 'f', 2)); xmlWriter->writeTextElement("CHLORIDE", QString::number(recipe->w1_chloride, 'f', 2)); xmlWriter->writeTextElement("SODIUM", QString::number(recipe->w1_sodium, 'f', 2)); xmlWriter->writeTextElement("PH", QString::number(recipe->w1_ph, 'f', 2)); xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(recipe->w1_total_alkalinity, 'f', 2)); xmlWriter->writeEndElement(); if (recipe->w2_amount > 0) { xmlWriter->writeStartElement("WATER"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->w2_name); xmlWriter->writeTextElement("AMOUNT", QString::number(recipe->w2_amount, 'f', 2)); xmlWriter->writeTextElement("CALCIUM", QString::number(recipe->w2_calcium, 'f', 2)); xmlWriter->writeTextElement("MAGNESIUM", QString::number(recipe->w2_magnesium, 'f', 2)); xmlWriter->writeTextElement("BICARBONATE", QString::number(recipe->w2_total_alkalinity * 1.22, 'f', 2)); xmlWriter->writeTextElement("SULFATE", QString::number(recipe->w2_sulfate, 'f', 2)); xmlWriter->writeTextElement("CHLORIDE", QString::number(recipe->w2_chloride, 'f', 2)); xmlWriter->writeTextElement("SODIUM", QString::number(recipe->w2_sodium, 'f', 2)); xmlWriter->writeTextElement("PH", QString::number(recipe->w2_ph, 'f', 2)); xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(recipe->w2_total_alkalinity, 'f', 2)); xmlWriter->writeEndElement(); } } xmlWriter->writeEndElement(); // WATERS xmlWriter->writeStartElement("MASH"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->mash_name); xmlWriter->writeTextElement("GRAIN_TEMP", "10.0"); xmlWriter->writeTextElement("PH", QString::number(recipe->sparge_ph, 'f', 2)); xmlWriter->writeTextElement("SPARGE_TEMP", QString::number(recipe->sparge_temp, 'f', 2)); xmlWriter->writeStartElement("MASH_STEPS"); for (int i = 0; i < recipe->mashs.size(); i++) { xmlWriter->writeStartElement("MASH_STEP"); xmlWriter->writeTextElement("VERSION", "1"); xmlWriter->writeTextElement("NAME", recipe->mashs.at(i).step_name); xmlWriter->writeTextElement("TYPE", g_step_types[recipe->mashs.at(i).step_type]); if (recipe->mashs.at(i).step_type == 0) { xmlWriter->writeTextElement("INFUSE_AMOUNT", QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 3)); xmlWriter->writeTextElement("INFUSE_TEMP", QString::number(recipe->mashs.at(i).step_infuse_temp, 'f', 3)); } if (recipe->mashs.at(i).step_type == 2) { xmlWriter->writeTextElement("DECOCTION_AMT", QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 3)); } xmlWriter->writeTextElement("STEP_TEMP", QString::number(recipe->mashs.at(i).step_temp, 'f', 1)); xmlWriter->writeTextElement("STEP_TIME", QString::number(recipe->mashs.at(i).step_time, 'f', 1)); xmlWriter->writeTextElement("RAMP_TIME", QString::number(recipe->mashs.at(i).ramp_time, 'f', 1)); xmlWriter->writeTextElement("END_TEMP", QString::number(recipe->mashs.at(i).end_temp, 'f', 1)); xmlWriter->writeTextElement("PH", QString::number(recipe->mash_ph, 'f', 1)); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); // MASH_STEPS xmlWriter->writeEndElement(); // MASH xmlWriter->writeEndElement(); // RECIPE xmlWriter->writeEndElement(); // RECIPES xmlWriter->writeEndDocument(); QMessageBox::information(this, tr("Save File"), tr("XML export ready")); file.close(); } void EditRecipe::copyRecipe() { Recipe *dup = new Recipe; dup = recipe; dup->record = -1; dup->uuid = ""; dup->name.append(" [duplicate]"); qDebug() << dup->record << dup->name; if (DB_recipe::save(dup, this)) { QMessageBox::information(this, tr("Copy Recipe"), tr("Copy Recipe export ready.")); } else { QMessageBox::warning(this, tr("Copy Recipe"), tr("Copy Recipe error.")); } delete dup; } void EditRecipe::copyProduct() { Product *p = new Product(); p->record = -1; p->name = recipe->name + QString(" [duplicate]"); p->code = ""; p->birth = QDate::currentDate(); p->stage = p->inventory_reduced = PROD_STAGE_PLAN; p->notes = recipe->notes; // p->log_brew = p->log_fermentation = p->log_ispindel = p->log_co2pressure = p->locked = false; p->st_name = recipe->st_name; p->st_letter = recipe->st_letter; p->st_guide = recipe->st_guide; p->st_category = recipe->st_category; p->st_category_number = recipe->st_category_number; p->st_type = recipe->st_type; p->st_og_min = recipe->st_og_min; p->st_og_max = recipe->st_og_max; p->st_fg_min = recipe->st_fg_min; p->st_fg_max = recipe->st_fg_max; p->st_ibu_min = recipe->st_ibu_min; p->st_ibu_max = recipe->st_ibu_max; p->st_color_min = recipe->st_color_min; p->st_color_max = recipe->st_color_max; p->st_carb_min = recipe->st_carb_min; p->st_carb_max = recipe->st_carb_max; p->st_abv_min = recipe->st_abv_min; p->st_abv_max = recipe->st_abv_max; p->eq_name = QString("Not yet set"); p->eq_notes = QString(""); p->eq_tun_specific_heat = 0.11; // p->eq_tun_material = 0; p->eq_tun_volume = p->eq_tun_height = 20; p->eq_tun_weight = 2; // p->eq_top_up_water = 0; p->eq_trub_chiller_loss = 0.5; p->eq_evap_rate = 1.8; p->eq_calc_boil_volume = true; // p->eq_top_up_kettle = 0; p->eq_hop_utilization = 100; p->eq_lauter_volume = p->eq_lauter_height = p->eq_kettle_volume = p->eq_kettle_height = p->eq_mash_volume = 20; p->eq_lauter_deadspace = 0.5; p->eq_mash_max = 6; p->eq_efficiency = p->efficiency = recipe->efficiency; p->eq_batch_size = p->batch_size = recipe->batch_size; p->eq_boil_time = p->boil_time = recipe->boil_time; p->eq_boil_size = p->boil_size = p->batch_size + (round(p->batch_size * p->boil_time / 60.0) / 10.0); p->type = 2; p->color_method = recipe->color_method; p->ibu_method = recipe->ibu_method; p->est_og = recipe->est_og; p->est_fg = recipe->est_fg; p->est_color = recipe->est_color; p->est_ibu = recipe->est_ibu; p->est_abv = recipe->est_abv; p->brew_date_start = p->brew_date_end = QDateTime(); // p->brew_mash_ph = p->brew_mash_sg = p->brew_mash_efficiency = 0; // p->brew_sparge_temperature = p->brew_sparge_volume = p->brew_sparge_est = p->brew_sparge_ph = 0; // p->brew_preboil_volume = p->brew_preboil_sg = p->brew_preboil_ph = p->brew_preboil_efficiency = 0; // p->brew_aboil_volume = p->brew_aboil_sg = p->brew_aboil_ph = p->brew_aboil_efficiency = 0; // p->brew_cooling_method = p->brew_cooling_time = 0; p->brew_cooling_to = 20; // p->brew_whirlpool9 = p->brew_whirlpool7 = p->brew_whirlpool6 = p->brew_whirlpool2 = 0; // p->brew_fermenter_volume = p->brew_fermenter_extrawater = p->brew_fermenter_tcloss = 0; // p->brew_aeration_time = p->brew_aeration_speed = p->brew_aeration_type = 0; // p->brew_fermenter_sg = p->brew_fermenter_ibu = p->brew_fermenter_color = 0; // p->og = p->fg = 0; // p->primary_start_temp = p->primary_max_temp = p->primary_end_temp = p->primary_end_sg = 0; p->primary_end_date = p->secondary_end_date = QDate(); // p->secondary_temp = p->secondary_end_sg = p->tertiary_temp = 0; p->package_date = QDate(); // p->package_volume = p->package_infuse_amount = p->package_infuse_abv = p->package_abv = p->package_ph = 0; p->package_infuse_notes = ""; // p->bottle_amount = p->bottle_carbonation = p->bottle_priming_amount = p->bottle_carbonation_temp = 0; // p->keg_amount = p->keg_carbonation = p->keg_priming_amount = p->keg_carbonation_temp = 0; // p->keg_pressure = 0; // p->bottle_priming_water = p->keg_priming_water = 0; // p->bottle_priming_sugar = p->keg_priming_sugar = 0; // p->taste_rate = 0; p->taste_date = QDate(); p->taste_notes = p->taste_color = p->taste_transparency = p->taste_head = ""; p->taste_aroma = p->taste_taste = p->taste_mouthfeel = p->taste_aftertaste = ""; p->sparge_temp = recipe->sparge_temp; p->sparge_ph = recipe->sparge_ph; p->sparge_volume = recipe->sparge_volume; p->sparge_source = recipe->sparge_source; p->sparge_acid_type = recipe->sparge_acid_type; p->sparge_acid_perc = recipe->sparge_acid_perc; p->sparge_acid_amount = recipe->sparge_acid_amount; p->mash_ph = recipe->mash_ph; p->mash_name = recipe->mash_name; p->calc_acid = recipe->calc_acid; p->w1_name = recipe->w1_name; p->w1_amount = recipe->w1_amount; p->w1_calcium = recipe->w1_calcium; p->w1_sulfate = recipe->w1_sulfate; p->w1_chloride = recipe->w1_chloride; p->w1_sodium = recipe->w1_sodium; p->w1_magnesium = recipe->w1_magnesium; p->w1_total_alkalinity = recipe->w1_total_alkalinity; p->w1_ph = recipe->w1_ph; p->w2_name = recipe->w2_name; p->w2_amount = recipe->w2_amount; p->w2_calcium = recipe->w2_calcium; p->w2_sulfate = recipe->w2_sulfate; p->w2_chloride = recipe->w2_chloride; p->w2_sodium = recipe->w2_sodium; p->w2_magnesium = recipe->w2_magnesium; p->w2_total_alkalinity = recipe->w2_total_alkalinity; p->w2_ph = recipe->w2_ph; p->wg_amount = recipe->wg_amount; p->wg_calcium = recipe->wg_calcium; p->wg_sulfate = recipe->wg_sulfate; p->wg_chloride = recipe->wg_chloride; p->wg_sodium = recipe->wg_sodium; p->wg_magnesium = recipe->wg_magnesium; p->wg_total_alkalinity = recipe->wg_total_alkalinity; p->wg_ph = recipe->wg_ph; p->wb_calcium = recipe->wb_calcium; p->wb_sulfate = recipe->wb_sulfate; p->wb_chloride = recipe->wb_chloride; p->wb_sodium = recipe->wb_sodium; p->wb_magnesium = recipe->wb_magnesium; p->wb_total_alkalinity = recipe->wb_total_alkalinity; p->wb_ph = recipe->wb_ph; p->wa_acid_name = recipe->wa_acid_name; p->wa_acid_perc = recipe->wa_acid_perc; p->wa_base_name = recipe->wa_base_name; p->starter_enable = false; // p->starter_type = p->prop_type[0] = p->prop_type[1] = p->prop_type[2] = p->prop_type[3] = 0; p->starter_viability = 100; p->starter_sg = 1.037; p->yeast_prod_date = QDate(); // p->yeast_pitchrate = p->prop_volume[0] = p->prop_volume[1] = p->prop_volume[2] = p->prop_volume[3] = 0; // p->divide_type = p->divide_parts = p->divide_part = 0; // p->divide_size = 0; p->divide_factor = 1; p->fermentables = recipe->fermentables; p->hops = recipe->hops; p->miscs = recipe->miscs; p->yeasts = recipe->yeasts; p->mashs = recipe->mashs; if (DB_product::save(p, this)) { QMessageBox::information(this, tr("Copy Recipe"), tr("Copy Recipe to Product ready.")); } else { QMessageBox::warning(this, tr("Copy Recipe"), tr("Copy Recipe to Product error.")); } delete p; } void EditRecipe::toforumRecipe() { const QStringList color_method({ "Morey", "Mosher", "Daniels", "Halberstadt", "Naudts" }); QString memo = QString("[u][b]BMSapp v"); memo.append(VERSIONSTRING); // For some stupid reason this must be on it's own. memo.append(" - Datum export: " + QDate::currentDate().toString("dd-MMM-yyyy") + "[/b][/u]\n\n\n"); memo.append("[u][b]Basis[/b][/u]\n[tabular]\n"); memo.append("[head]Omschrijving[/head][head]Waarde[/head]\n"); memo.append("[row][data]Bier naam[/data][data]" + recipe->name + "[/data][/row]\n"); memo.append("[row][data]Bier stijl[/data][data]" + recipe->st_name + "[/data][/row]\n"); memo.append("[row][data]Recept type[/data][data]" + QCoreApplication::translate("RecipeType", g_recipe_types[recipe->type]) + "[/data][/row]\n"); memo.append("[row][data]Batch grootte[/data][data]" + QString::number(recipe->batch_size, 'f', 1) + " L[/data][/row]\n"); memo.append("[row][data]Kooktijd[/data][data]" + QString::number(recipe->boil_time, 'f', 0) + " minuten[/data][/row]\n"); memo.append("[row][data]Brouwzaal rendement[/data][data]" + QString::number(recipe->efficiency, 'f', 1) + "%[/data][/row]\n"); memo.append("[row][data]Geschatte begin densiteit[/data][data]" + QString::number(recipe->est_og, 'f', 3) + " SG[/data][/row]\n"); memo.append("[row][data]Geschatte eind densiteit[/data][data]" + QString::number(recipe->est_fg, 'f', 3) + " SG[/data][/row]\n"); memo.append("[row][data]Geschat alcohol[/data][data]" + QString::number(recipe->est_abv, 'f', 1) + "%[/data][/row]\n"); memo.append("[row][data]Kleur (" + color_method[recipe->color_method] + ")[/data][data]" + QString::number(recipe->est_color, 'f', 0) + " EBC[/data][/row]\n"); memo.append("[row][data]Bitterheid (" + QString(g_ibu_method[recipe->ibu_method]) + ")[/data][data]" + QString::number(recipe->est_ibu, 'f', 1) + " IBU[/data][/row]\n"); memo.append("[/tabular]\n\n"); memo.append("[u][b]Vergistbare ingrediënten[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Mout, granen en suikers[/head][head]EBC[/head][head]Gewicht kg[/head][head]%[/head][head]Gebruik tijdens[/head]\n"); for (int i = 0; i < recipe->fermentables.size(); i++) { memo.append("[row][data]" + recipe->fermentables.at(i).name + " (" + recipe->fermentables.at(i).supplier + ")[/data]"); memo.append("[data]" + QString::number(recipe->fermentables.at(i).color) + "[/data]"); memo.append("[data]" + QString::number(recipe->fermentables.at(i).amount, 'f', 3) + "[/data]"); memo.append("[data]" + QString::number(recipe->fermentables.at(i).percentage, 'f', 1) + "[/data]"); memo.append("[data]" + QCoreApplication::translate("FermentableAdded", g_fermentable_added[recipe->fermentables.at(i).added]) + "[/data][/row]\n"); } memo.append("[/tabular]\n\n"); memo.append("[u][b]Hop[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Hop[/head][head]Vorm[/head][head]Alpha[/head][head]IBU[/head][head]Gram[/head][head]Toevoegen moment[/head]\n"); for (int i = 0; i < recipe->hops.size(); i++) { double ibu = Utils::toIBU(recipe->hops.at(i).useat, recipe->hops.at(i).form, recipe->preboil_sg, recipe->est_og, recipe->batch_size, recipe->hops.at(i).amount, recipe->hops.at(i).time, recipe->hops.at(i).alpha, recipe->ibu_method, 0, recipe->hops.at(i).time, 0, recipe->boil_time, 0, 0, 0, recipe->hops.at(i).utilisation, recipe->hops.at(i).bu_factor); memo.append("[row][data]" + recipe->hops.at(i).name + " (" + recipe->hops.at(i).origin + ")[/data]"); memo.append("[data]" + QCoreApplication::translate("HopForm", g_hop_forms[recipe->hops.at(i).form]) + "[/data]"); memo.append("[data]" + QString::number(recipe->hops.at(i).alpha, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(ibu, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->hops.at(i).amount * 1000, 'f', 2) + "[/data]"); if (recipe->hops.at(i).useat == HOP_USEAT_BOIL || recipe->hops.at(i).useat == HOP_USEAT_WHIRLPOOL) memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + " " + QString::number(recipe->hops.at(i).time) + " minuten[/data][/row]\n"); else if (recipe->hops.at(i).useat == HOP_USEAT_DRY_HOP) memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + " " + QString::number(recipe->hops.at(i).time / 1440) + " dagen[/data][/row]\n"); else memo.append("[data]" + QCoreApplication::translate("HopUse", g_hop_useat[recipe->hops.at(i).useat]) + "[/data][/row]\n"); } memo.append("[/tabular]\n\n"); memo.append("[u][b]Diversen[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Specerij, kruid, brouwzout[/head][head]Type grondstof[/head][head]Gebruik tijdens[/head][head]Hoeveel[/head]\n"); for (int i = 0; i < recipe->miscs.size(); i++) { memo.append("[row][data]" + recipe->miscs.at(i).name + "[/data]"); memo.append("[data]" + QCoreApplication::translate("MiscType", g_misc_types[recipe->miscs.at(i).type]) + "[/data]"); if (recipe->miscs.at(i).use_use == MISC_USES_BOIL) memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[recipe->miscs.at(i).use_use]) + " " + QString::number(recipe->miscs.at(i).time) + " min[/data]"); else memo.append("[data]" + QCoreApplication::translate("MiscUse", g_misc_uses[recipe->miscs.at(i).use_use]) + "[/data]"); memo.append("[data]"+QString::number(recipe->miscs.at(i).amount * 1000, 'f', 2)+((recipe->miscs.at(i).amount_is_weight)?" gr":" ml")+"[/data][/row]\n"); } memo.append("[/tabular]\n\n"); memo.append("[u][b]Gist[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Gistlab en code[/head][head]Omschrijving[/head][head]Gebruik[/head][head]Vorm[/head][head]Hoeveel[/head]\n"); for (int i = 0; i < recipe->yeasts.size(); i++) { memo.append("[row][data]" + recipe->yeasts.at(i).laboratory + " " + recipe->yeasts.at(i).product_id + "[/data]"); memo.append("[data]" + recipe->yeasts.at(i).name + "[/data]"); memo.append("[data]" + QCoreApplication::translate("YeastUse", g_yeast_use[recipe->yeasts.at(i).use]) + "[/data]"); memo.append("[data]" + QCoreApplication::translate("YeastForm", g_yeast_forms[recipe->yeasts.at(i).form]) + "[/data]"); if (recipe->yeasts.at(i).form == YEAST_FORMS_LIQUID) memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount, 'f', 0) + " pak[/data][/row]\n"); else if (recipe->yeasts.at(i).form == YEAST_FORMS_DRY || recipe->yeasts.at(i).form == YEAST_FORMS_DRIED) memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount * 1000, 'f', 1) + " gr[/data][/row]\n"); else memo.append("[data]" + QString::number(recipe->yeasts.at(i).amount * 1000, 'f', 0) + " ml[/data][/row]\n"); } memo.append("[/tabular]\n\n"); memo.append("[u][b]Maischen[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Maisch stap[/head][head]Stap type[/head][head]Temperatuur[/head][head]Rust tijd[/head][head]Opwarm tijd[/head]\n"); for (int i = 0; i < recipe->mashs.size(); i++) { memo.append("[row][data]" + recipe->mashs.at(i).step_name + "[/data]"); if (recipe->mashs.at(i).step_type != 1) memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[recipe->mashs.at(i).step_type])+" "+QString::number(recipe->mashs.at(i).step_infuse_amount, 'f', 1) + " L[/data]"); else memo.append("[data]" + QCoreApplication::translate("StepType", g_step_types[recipe->mashs.at(i).step_type]) + "[/data]"); memo.append("[data]" + QString::number(recipe->mashs.at(i).step_temp, 'f', 1) + " °C[/data]"); memo.append("[data]" + QString::number(recipe->mashs.at(i).step_time, 'f', 0) + " min[/data]"); memo.append("[data]" + QString::number(recipe->mashs.at(i).ramp_time, 'f', 0) + " min[/data][/row]\n"); } memo.append("[/tabular]\n\n"); memo.append("[u][b]Brouwwater[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Omschrijving[/head][head]Waarde[/head]\n"); if (recipe->w2_name != "" && recipe->w2_amount > 0) { memo.append("[row][data]Maischwater 1[/data][data]" + recipe->w1_name + " " + QString::number(recipe->w1_amount, 'f', 1) + " Liter[/data][/row]\n"); memo.append("[row][data]Maischwater 2[/data][data]" + recipe->w2_name + " " + QString::number(recipe->w2_amount, 'f', 1) + " Liter[/data][/row]\n"); } else { memo.append("[row][data]Maischwater[/data][data]" + recipe->w1_name + " " + QString::number(recipe->w1_amount, 'f', 1) + " Liter[/data][/row]\n"); } memo.append("[row][data]Maischwater aanzuren tot[/data][data]" + QString::number(recipe->mash_ph, 'f', 1) + " pH[/data][/row]\n"); memo.append("[row][data]Spoelwater geschat[/data][data]" + QString::number(recipe->sparge_volume, 'f', 1) + " Liter[/data][/row]\n"); memo.append("[row][data]Spoelwater temperatuur[/data][data]" + QString::number(recipe->sparge_temp, 'f', 1) + " °C[/data][/row]\n"); memo.append("[row][data]Spoelwater aanzuren tot[/data][data]" + QString::number(recipe->sparge_ph, 'f', 1) + " pH[/data][/row]\n"); memo.append("[/tabular]\n\n"); memo.append("[u][b]Waterprofiel behandeld water[/b][/u]\n"); memo.append("[tabular]\n"); memo.append("[head]Ca[/head][head]Mg[/head][head]Na[/head][head]HCO3[/head][head]Cl[/head][head]SO4[/head]\n"); memo.append("[row][data]" + QString::number(recipe->wb_calcium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->wb_magnesium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->wb_sodium, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->wb_total_alkalinity * 61 / 50, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->wb_chloride, 'f', 1) + "[/data]"); memo.append("[data]" + QString::number(recipe->wb_sulfate, 'f', 1) + "[/data][/row]\n"); memo.append("[/tabular]\n\n"); qDebug().noquote() << memo; QGuiApplication::clipboard()->setText(memo, QClipboard::Clipboard); QGuiApplication::clipboard()->setText(memo, QClipboard::Selection); QMessageBox::information(this, tr("Export to forum"), tr("The recipe and all data are copied to the clipboard.\n" "You can \"paste\" this data in the forum screen in your web browser.")); } void EditRecipe::on_exportButton_clicked() { QDialog* dialog = new QDialog(this); dialog->setWindowTitle(tr("Export choices")); dialog->setObjectName(QString::fromUtf8("Dialog")); dialog->resize(400, 149); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(280, 20, 81, 61)); buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); QRadioButton *beerxmlButton = new QRadioButton(dialog); beerxmlButton->setObjectName(QString::fromUtf8("beerxmlButton")); beerxmlButton->setGeometry(QRect(50, 20, 171, 21)); beerxmlButton->setText(tr("Export to beerXML")); QRadioButton *copy_recipeButton = new QRadioButton(dialog); copy_recipeButton->setObjectName(QString::fromUtf8("copy_recipeButton")); copy_recipeButton->setGeometry(QRect(50, 50, 171, 21)); copy_recipeButton->setText(tr("Copy to recipe")); QRadioButton *copy_productButton = new QRadioButton(dialog); copy_productButton->setObjectName(QString::fromUtf8("copy_productButton")); copy_productButton->setGeometry(QRect(50, 80, 171, 21)); copy_productButton->setText(tr("Copy to product")); QRadioButton *toforumButton = new QRadioButton(dialog); toforumButton->setObjectName(QString::fromUtf8("toforumButton")); toforumButton->setGeometry(QRect(50, 110, 171, 21)); toforumButton->setText(tr("Export to forum")); QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); dialog->setModal(true); dialog->exec(); if (dialog->result() == QDialog::Accepted) { if (beerxmlButton->isChecked()) exportBeerXML(); if (copy_recipeButton->isChecked()) copyRecipe(); if (copy_productButton->isChecked()) copyProduct(); if (toforumButton->isChecked()) toforumRecipe(); } disconnect(buttonBox, nullptr, nullptr, nullptr); } void EditRecipe::on_printButton_clicked() { PrinterDialog(PR_RECIPE, -1, this); }