From f65ae1d2a9e3923a8dd330e3e274a1e5b7b9cbb8 Mon Sep 17 00:00:00 2001 From: Sepand Haghighi Date: Fri, 4 Oct 2024 10:00:29 +0330 Subject: [PATCH] Ratio limits (#28) * feat : ratio_lower_limit and ratio_upper_limit added to METHODS_MAP * doc : METHODS.md updated * feat : check_ratio_limits function added * fix : use Fraction for ratios * fix : minor edit in check_ratio_limits function * doc : CHANGELOG.md updated * fix : Fraction bug fixed * fix : RATIO_WARNING_MESSAGE updated * fix : functions_test.py updated * fix : verified_test.py updated * fix : autopep8 * fix : verified_test.py updated * fix : functions_test.py updated --- CHANGELOG.md | 4 +++ METHODS.md | 42 +++++++++++++++++++++++- mycoffee/functions.py | 19 +++++++++++ mycoffee/params.py | 38 ++++++++++++++++++++++ test/functions_test.py | 26 +++++++++++++++ test/verified_test.py | 74 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b99da3c..66cf91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Ratio upper limit +- Ratio lower limit +- `check_ratio_limits` function ### Changed - Test system modified ## [0.4] - 2024-10-01 diff --git a/METHODS.md b/METHODS.md index 3ddeb0c..fafc8ca 100644 --- a/METHODS.md +++ b/METHODS.md @@ -4,7 +4,9 @@ Title Code - Ratio + Ratio(Recommended) + Ratio(Lower Limit) + Ratio(Upper Limit) Water(gr) Version @@ -12,6 +14,8 @@ Custom custom 1/17 + -- + -- 240 >=0.1 @@ -19,6 +23,8 @@ V60 v60 3/50 + 1/18 + 1/14 250 >=0.1 @@ -26,6 +32,8 @@ Chemex chemex 1/15 + 1/21 + 1/10 240 >=0.1 @@ -33,6 +41,8 @@ Espresso espresso 1/2 + 1/2.5 + 1/1.5 36 >=0.1 @@ -40,6 +50,8 @@ French press french-press 1/15 + 1/18 + 1/12 120 >=0.1 @@ -47,6 +59,8 @@ Siphon siphon 1/15 + 1/16 + 1/12 240 >=0.1 @@ -54,6 +68,8 @@ Pour-over pour-over 1/15 + 1/16 + 1/14 240 >=0.2 @@ -61,6 +77,8 @@ Auto drip auto-drip 1/16 + 1/17 + 1/14 128 >=0.2 @@ -68,6 +86,8 @@ Cold brew cold-brew 1/11 + 1/15 + 1/8 242 >=0.2 @@ -75,6 +95,8 @@ Cold brew concentrate cold-brew-conc 1/5 + 1/6 + 1/4 120 >=0.2 @@ -82,6 +104,8 @@ Moka pot moka-pot 1/10 + 1/12 + 1/7 60 >=0.2 @@ -89,6 +113,8 @@ Ristretto ristretto 1/1 + 1/1.5 + 1/1 18 >=0.3 @@ -96,6 +122,8 @@ Lungo lungo 1/4 + 1/4 + 1/2.5 72 >=0.3 @@ -103,6 +131,8 @@ Turkish turkish 1/10 + 1/12 + 1/8 50 >=0.3 @@ -110,6 +140,8 @@ Cupping cupping 11/200 + 1/19 + 1/17 150 >=0.3 @@ -117,6 +149,8 @@ AeroPress standard aero-press 1/15 + 1/18 + 1/12 135 >=0.4 @@ -124,6 +158,8 @@ AeroPress concentrate aero-press-conc 1/6 + 1/7 + 1/5 90 >=0.4 @@ -131,6 +167,8 @@ AeroPress inverted aero-press-inv 1/12 + 1/14 + 1/10 132 >=0.4 @@ -138,6 +176,8 @@ Steep-and-release steep-and-release 1/16 + 1/17 + 1/14 255 >=0.4 diff --git a/mycoffee/functions.py b/mycoffee/functions.py index 609e265..7377b3b 100644 --- a/mycoffee/functions.py +++ b/mycoffee/functions.py @@ -2,6 +2,7 @@ """mycoffee functions.""" from mycoffee.params import MESSAGE_TEMPLATE, METHODS_LIST_TEMPLATE, EMPTY_INFO from mycoffee.params import MY_COFFEE_VERSION, DEFAULT_PARAMS, METHODS_MAP +from mycoffee.params import RATIO_WARNING_MESSAGE from art import tprint @@ -36,6 +37,7 @@ def print_message(params): params["coffee_ratio"], params["water_ratio"], params["info"])) + check_ratio_limits(params) def load_method_params(method_name): @@ -109,6 +111,23 @@ def filter_params(params): return params +def check_ratio_limits(params): + """ + Check ratio limits. + + :param params: parameters + :type params: dict + :return: None + """ + method = params["method"] + if "ratio_lower_limit" in METHODS_MAP[method] and "ratio_upper_limit" in METHODS_MAP[method]: + ratio = params["coffee_ratio"] / params["water_ratio"] + ratio_lower_limit = METHODS_MAP[method]["ratio_lower_limit"] + ratio_upper_limit = METHODS_MAP[method]["ratio_upper_limit"] + if ratio < ratio_lower_limit or ratio > ratio_upper_limit: + print(RATIO_WARNING_MESSAGE.format(method, str(ratio_lower_limit), str(ratio_upper_limit))) + + def calc_coffee(params): """ Calculate coffee. diff --git a/mycoffee/params.py b/mycoffee/params.py index 6db38d2..ac77e0c 100644 --- a/mycoffee/params.py +++ b/mycoffee/params.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- """mycoffee params.""" +from fractions import Fraction MY_COFFEE_VERSION = "0.4" INPUT_ERROR_MESSAGE = "[Error] Wrong input" +RATIO_WARNING_MESSAGE = "[Warning] The ratio is not within the standard range. For `{0}`, the ratio can be anywhere between `{1}` and `{2}`" INPUT_EXAMPLE = "Example: mycoffee --method=v60" EXIT_MESSAGE = "See you. Bye!" EMPTY_INFO = "Nothing :)" @@ -44,108 +46,144 @@ "v60": { "coffee_ratio": 3, "water_ratio": 50, + "ratio_lower_limit": Fraction(1, 18), + "ratio_upper_limit": Fraction(1, 14), "water": 250, "info": "V60 method" }, "espresso": { "coffee_ratio": 1, "water_ratio": 2, + "ratio_lower_limit": Fraction(2, 5), + "ratio_upper_limit": Fraction(2, 3), "water": 36, "info": "Espresso method" }, "ristretto": { "coffee_ratio": 1, "water_ratio": 1, + "ratio_lower_limit": Fraction(2, 3), + "ratio_upper_limit": Fraction(1, 1), "water": 18, "info": "Ristretto method" }, "lungo": { "coffee_ratio": 1, "water_ratio": 4, + "ratio_lower_limit": Fraction(1, 4), + "ratio_upper_limit": Fraction(2, 5), "water": 72, "info": "Lungo method" }, "chemex": { "coffee_ratio": 1, "water_ratio": 15, + "ratio_lower_limit": Fraction(1, 21), + "ratio_upper_limit": Fraction(1, 10), "water": 240, "info": "Chemex method" }, "french-press": { "coffee_ratio": 1, "water_ratio": 15, + "ratio_lower_limit": Fraction(1, 18), + "ratio_upper_limit": Fraction(1, 12), "water": 120, "info": "French press method" }, "siphon": { "coffee_ratio": 1, "water_ratio": 15, + "ratio_lower_limit": Fraction(1, 16), + "ratio_upper_limit": Fraction(1, 12), "water": 240, "info": "Siphon method" }, "pour-over": { "coffee_ratio": 1, "water_ratio": 15, + "ratio_lower_limit": Fraction(1, 16), + "ratio_upper_limit": Fraction(1, 14), "water": 240, "info": "Pour-over method" }, "auto-drip": { "coffee_ratio": 1, "water_ratio": 16, + "ratio_lower_limit": Fraction(1, 17), + "ratio_upper_limit": Fraction(1, 14), "water": 128, "info": "Auto drip method" }, "cold-brew": { "coffee_ratio": 1, "water_ratio": 11, + "ratio_lower_limit": Fraction(1, 15), + "ratio_upper_limit": Fraction(1, 8), "water": 242, "info": "Cold brew method" }, "cold-brew-conc": { "coffee_ratio": 1, "water_ratio": 5, + "ratio_lower_limit": Fraction(1, 6), + "ratio_upper_limit": Fraction(1, 4), "water": 120, "info": "Cold brew concentrate method" }, "moka-pot": { "coffee_ratio": 1, "water_ratio": 10, + "ratio_lower_limit": Fraction(1, 12), + "ratio_upper_limit": Fraction(1, 7), "water": 60, "info": "Moka pot method" }, "turkish": { "coffee_ratio": 1, "water_ratio": 10, + "ratio_lower_limit": Fraction(1, 12), + "ratio_upper_limit": Fraction(1, 8), "water": 50, "info": "Turkish method" }, "cupping": { "coffee_ratio": 11, "water_ratio": 200, + "ratio_lower_limit": Fraction(1, 19), + "ratio_upper_limit": Fraction(1, 17), "water": 150, "info": "Cupping method" }, "aero-press": { "coffee_ratio": 1, "water_ratio": 15, + "ratio_lower_limit": Fraction(1, 18), + "ratio_upper_limit": Fraction(1, 12), "water": 135, "info": "AeroPress standard method" }, "aero-press-conc": { "coffee_ratio": 1, "water_ratio": 6, + "ratio_lower_limit": Fraction(1, 7), + "ratio_upper_limit": Fraction(1, 5), "water": 90, "info": "AeroPress concentrate method" }, "aero-press-inv": { "coffee_ratio": 1, "water_ratio": 12, + "ratio_lower_limit": Fraction(1, 14), + "ratio_upper_limit": Fraction(1, 10), "water": 132, "info": "AeroPress inverted method" }, "steep-and-release": { "coffee_ratio": 1, "water_ratio": 16, + "ratio_lower_limit": Fraction(1, 17), + "ratio_upper_limit": Fraction(1, 14), "water": 255, "info": "Steep-and-release method" } diff --git a/test/functions_test.py b/test/functions_test.py index 51d06c2..d60701f 100644 --- a/test/functions_test.py +++ b/test/functions_test.py @@ -46,6 +46,32 @@ Info: Nothing :) +>>> test_params = {"method":"v60", "cups":2, "coffee":3, "water":500, "coffee_ratio": 6, "water_ratio":1000, "info":"", "digits":3} +>>> test_params = filter_params(test_params) +>>> print_message(test_params) + __ __ _ _ ___ _____ ____ ____ ____ ____ +( \/ )( \/ ) / __)( _ )( ___)( ___)( ___)( ___) + ) ( \ / ( (__ )(_)( )__) )__) )__) )__) +(_/\/\_) (__) \___)(_____)(__) (__) (____)(____) + + + +Method: `v60` + +Cups: 2 + +Coffee: 3 gr + +Water: 500 gr + +Ratio: 6/1000 + +Info: Nothing :) + +[Warning] The ratio is not within the standard range. For `v60`, the ratio can be anywhere between `1/18` and `1/14` +>>> test_params = {"method":"custom", "cups":2, "coffee":3, "water":500, "coffee_ratio": 6, "water_ratio":1000, "info":"", "digits":3} +>>> test_params = filter_params(test_params) +>>> check_ratio_limits(test_params) >>> chemex_params = load_method_params("chemex") >>> chemex_params == {'info': 'Chemex method', 'water': 240, 'cups': 1, 'coffee_ratio': 1, 'water_ratio': 15, 'digits': 3} True diff --git a/test/verified_test.py b/test/verified_test.py index 47e00f3..24e4fe0 100644 --- a/test/verified_test.py +++ b/test/verified_test.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """ +>>> from fractions import Fraction >>> from mycoffee.functions import * +>>> from mycoffee.params import METHODS_MAP >>> v60_params = load_method_params("v60") # https://www.origincoffee.co.uk/blogs/journal/brewing-at-home-v60 >>> v60_params["coffee_ratio"] == 3 True @@ -11,6 +13,10 @@ >>> v60_coffee = calc_coffee(v60_params) >>> v60_coffee == 15 True +>>> METHODS_MAP["v60"]["ratio_upper_limit"] == Fraction(1, 14) +True +>>> METHODS_MAP["v60"]["ratio_lower_limit"] == Fraction(1, 18) +True >>> chemex_params = load_method_params("chemex") # https://honestcoffeeguide.com/coffee-to-water-ratio-calculator >>> chemex_params["coffee_ratio"] == 1 True @@ -21,6 +27,10 @@ >>> chemex_coffee = calc_coffee(chemex_params) >>> chemex_coffee == 16 True +>>> METHODS_MAP["chemex"]["ratio_upper_limit"] == Fraction(1, 10) +True +>>> METHODS_MAP["chemex"]["ratio_lower_limit"] == Fraction(1, 21) +True >>> espresso_params = load_method_params("espresso") # https://honestcoffeeguide.com/coffee-to-water-ratio-calculator >>> espresso_params["coffee_ratio"] == 1 True @@ -31,6 +41,10 @@ >>> espresso_coffee = calc_coffee(espresso_params) >>> espresso_coffee == 18 True +>>> METHODS_MAP["espresso"]["ratio_upper_limit"] == Fraction(2, 3) +True +>>> METHODS_MAP["espresso"]["ratio_lower_limit"] == Fraction(2, 5) +True >>> siphon_params = load_method_params("siphon") # https://bluebottlecoffee.com/us/eng/brew-guides/siphon >>> siphon_params["coffee_ratio"] == 1 True @@ -41,6 +55,10 @@ >>> siphon_coffee = calc_coffee(siphon_params) >>> siphon_coffee == 16 True +>>> METHODS_MAP["siphon"]["ratio_upper_limit"] == Fraction(1, 12) +True +>>> METHODS_MAP["siphon"]["ratio_lower_limit"] == Fraction(1, 16) +True >>> french_press_params = load_method_params("french-press") # https://useandcares.hamiltonbeach.com/files/840230401.pdf >>> french_press_params["coffee_ratio"] == 1 True @@ -51,6 +69,10 @@ >>> french_press_coffee = calc_coffee(french_press_params) >>> french_press_coffee == 8 True +>>> METHODS_MAP["french-press"]["ratio_upper_limit"] == Fraction(1, 12) +True +>>> METHODS_MAP["french-press"]["ratio_lower_limit"] == Fraction(1, 18) +True >>> pour_over_params = load_method_params("pour-over") # https://www.nicolebattefeld.com/post/best-recipes-2022 >>> pour_over_params["coffee_ratio"] == 1 True @@ -61,6 +83,10 @@ >>> pour_over_coffee = calc_coffee(pour_over_params) >>> pour_over_coffee == 16 True +>>> METHODS_MAP["pour-over"]["ratio_upper_limit"] == Fraction(1, 14) +True +>>> METHODS_MAP["pour-over"]["ratio_lower_limit"] == Fraction(1, 16) +True >>> auto_drip_params = load_method_params("auto-drip") # https://wonderstate.com/pages/auto-drip >>> auto_drip_params["coffee_ratio"] == 1 True @@ -71,6 +97,10 @@ >>> auto_drip_coffee = calc_coffee(auto_drip_params) >>> auto_drip_coffee == 8 True +>>> METHODS_MAP["auto-drip"]["ratio_upper_limit"] == Fraction(1, 14) +True +>>> METHODS_MAP["auto-drip"]["ratio_lower_limit"] == Fraction(1, 17) +True >>> cold_brew_params = load_method_params("cold-brew") # https://counterculturecoffee.com/blogs/counter-culture-coffee/guide-to-cold-brew >>> cold_brew_params["coffee_ratio"] == 1 True @@ -81,6 +111,10 @@ >>> cold_brew_coffee = calc_coffee(cold_brew_params) >>> cold_brew_coffee == 22 True +>>> METHODS_MAP["cold-brew"]["ratio_upper_limit"] == Fraction(1, 8) +True +>>> METHODS_MAP["cold-brew"]["ratio_lower_limit"] == Fraction(1, 15) +True >>> cold_brew_conc_params = load_method_params("cold-brew-conc") # https://www.thespruceeats.com/cold-brew-concentrate-recipe-5197494 >>> cold_brew_conc_params["coffee_ratio"] == 1 True @@ -91,6 +125,10 @@ >>> cold_brew_conc_coffee = calc_coffee(cold_brew_conc_params) >>> cold_brew_conc_coffee == 24 True +>>> METHODS_MAP["cold-brew-conc"]["ratio_upper_limit"] == Fraction(1, 4) +True +>>> METHODS_MAP["cold-brew-conc"]["ratio_lower_limit"] == Fraction(1, 6) +True >>> moka_pot_params = load_method_params("moka-pot") # https://bakedbrewedbeautiful.com/how-to-make-coffee-in-moka-pot >>> moka_pot_params["coffee_ratio"] == 1 True @@ -101,6 +139,10 @@ >>> moka_pot_coffee = calc_coffee(moka_pot_params) >>> moka_pot_coffee == 6 True +>>> METHODS_MAP["moka-pot"]["ratio_upper_limit"] == Fraction(1, 7) +True +>>> METHODS_MAP["moka-pot"]["ratio_lower_limit"] == Fraction(1, 12) +True >>> ristretto_params = load_method_params("ristretto") # https://honestcoffeeguide.com/coffee-to-water-ratio-calculator >>> ristretto_params["coffee_ratio"] == 1 True @@ -111,6 +153,10 @@ >>> ristretto_coffee = calc_coffee(ristretto_params) >>> ristretto_coffee == 18 True +>>> METHODS_MAP["ristretto"]["ratio_upper_limit"] == Fraction(1, 1) +True +>>> METHODS_MAP["ristretto"]["ratio_lower_limit"] == Fraction(2, 3) +True >>> lungo_params = load_method_params("lungo") # https://honestcoffeeguide.com/coffee-to-water-ratio-calculator >>> lungo_params["coffee_ratio"] == 1 True @@ -121,6 +167,10 @@ >>> lungo_coffee = calc_coffee(lungo_params) >>> lungo_coffee == 18 True +>>> METHODS_MAP["lungo"]["ratio_upper_limit"] == Fraction(2, 5) +True +>>> METHODS_MAP["lungo"]["ratio_lower_limit"] == Fraction(1, 4) +True >>> turkish_params = load_method_params("turkish") # https://www.drinktrade.com/blogs/education/how-to-make-turkish-coffee >>> turkish_params["coffee_ratio"] == 1 True @@ -131,6 +181,10 @@ >>> turkish_coffee = calc_coffee(turkish_params) >>> turkish_coffee == 5 True +>>> METHODS_MAP["turkish"]["ratio_upper_limit"] == Fraction(1, 8) +True +>>> METHODS_MAP["turkish"]["ratio_lower_limit"] == Fraction(1, 12) +True >>> cupping_params = load_method_params("cupping") # https://www.horshamcoffeeroaster.co.uk/pages/how-to-cup-coffee >>> cupping_params["coffee_ratio"] == 11 True @@ -141,6 +195,10 @@ >>> cupping_coffee = calc_coffee(cupping_params) >>> cupping_coffee == 8.25 True +>>> METHODS_MAP["cupping"]["ratio_upper_limit"] == Fraction(1, 17) +True +>>> METHODS_MAP["cupping"]["ratio_lower_limit"] == Fraction(1, 19) +True >>> aero_press_params = load_method_params("aero-press") # https://aeroprecipe.com/recipes/tetsu-kasuya-aeropress-recipe >>> aero_press_params["coffee_ratio"] == 1 True @@ -151,6 +209,10 @@ >>> aero_press_coffee = calc_coffee(aero_press_params) >>> aero_press_coffee == 9 True +>>> METHODS_MAP["aero-press"]["ratio_upper_limit"] == Fraction(1, 12) +True +>>> METHODS_MAP["aero-press"]["ratio_lower_limit"] == Fraction(1, 18) +True >>> aero_press_conc_params = load_method_params("aero-press-conc") # https://www.seattlecoffeegear.com/pages/product-resource/aero-press-product-resources >>> aero_press_conc_params["coffee_ratio"] == 1 True @@ -161,6 +223,10 @@ >>> aero_press_conc_coffee = calc_coffee(aero_press_conc_params) >>> aero_press_conc_coffee == 15 True +>>> METHODS_MAP["aero-press-conc"]["ratio_upper_limit"] == Fraction(1, 5) +True +>>> METHODS_MAP["aero-press-conc"]["ratio_lower_limit"] == Fraction(1, 7) +True >>> aero_press_inv_params = load_method_params("aero-press-inv") # https://aeroprecipe.com/recipes/all-about-the-intervals >>> aero_press_inv_params["coffee_ratio"] == 1 True @@ -171,6 +237,10 @@ >>> aero_press_inv_coffee = calc_coffee(aero_press_inv_params) >>> aero_press_inv_coffee == 11 True +>>> METHODS_MAP["aero-press-inv"]["ratio_upper_limit"] == Fraction(1, 10) +True +>>> METHODS_MAP["aero-press-inv"]["ratio_lower_limit"] == Fraction(1, 14) +True >>> steep_and_release_params = load_method_params("steep-and-release") # https://squaremileblog.com/brew-guide-clever-dripper/ >>> steep_and_release_params["coffee_ratio"] == 1 True @@ -181,4 +251,8 @@ >>> steep_and_release_coffee = calc_coffee(steep_and_release_params) >>> steep_and_release_coffee == 15.9375 True +>>> METHODS_MAP["steep-and-release"]["ratio_upper_limit"] == Fraction(1, 14) +True +>>> METHODS_MAP["steep-and-release"]["ratio_lower_limit"] == Fraction(1, 17) +True """