repl.it
@JenniferGottsc1/

Recipe_Moderniser_Complete

Python

Scales recipe and converts *most* ingredients into grams

fork
loading
Files
  • main.py
  • 01_ingredients_ml_to_g.csv
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# modules to be used...
import csv
import re

# ***** Functions ******

def not_blank(question, error_msg, num_ok):
    error = error_msg

    valid = False
    while not valid:
        response = input(question)
        has_errors = ""

        if num_ok != "yes":
            # look at each character in string and if it's a number, complain
            for letter in response:
                if letter.isdigit() == True:
                    has_errors = "yes"
                    break

        if response == "":
            print(error)
            continue
        elif has_errors != "":
            print(error)
            continue
        else:
            return response

# Number checking function (number must be a float that is more than 0)
def num_check(question):

    error = "Please enter a number that is more than zero"

    valid = False
    while not valid:
        response = input(question)

        if response == "":
            print(error)
            continue

        try:
            response = eval(response)
            response = float(response)

            if response <= 0:
                print(error)
            else:
                return response

        except NameError:
            print(error)

        except ValueError:
            print(error)

        except SyntaxError:
            print(error)

def yes_no_check(question):
    error = "Please enter 'yes' or 'no'"

    valid = False
    while not valid:
        response = input(question).lower()

        if response == "y" or response == "yes":
            return ("yes")
        elif response == "n" or response == "no":
            return ("no")
        else:
            print(error)

# Function to get (and check) scale factor
def get_sf():

    serving_size = num_check("What is the recipe serving size? ")

    # Main Routine goes here
    dodgy_sf = "yes"
    while dodgy_sf == "yes":

        desired_size = num_check("How many servings are needed? ")

        scale_factor = desired_size / serving_size

        if scale_factor < 0.25:
            dodgy_sf = input("Warning: This scale factor is very small and you "
                          "might struggle to accurately weigh the ingredients.  \n"
                          "Do you want to fix this and make more servings? ").lower()
        elif scale_factor > 4:
            dodgy_sf = input("Warning: This scale factor is quite large - you might "
                          "have issues with mixing bowl volumes and oven space.  "
                          "\nDo you want to fix this and make a smaller "
                          "batch? ").lower()
        else:
            dodgy_sf = "no"

    return scale_factor

# Function to get (and check amount, unit and ingredient)
def get_all_ingredients():
    all_ingredients = []

    stop = ""
    print("Please enter ingredients one line at a time.  Press 'xxx' to when "
          "you are done.")
    print()
    while stop != "xxx":
        # Ask user for ingredient (via not blank function)
        get_recipe_line = not_blank("Recipe Line <or 'xxx' to end>: ",
                                   "This can't be blank",
                                   "yes")

        # Stop loopin if exit code is typed and there are more
        # than 2 ingredients...
        if get_recipe_line.lower() == "xxx" and len(all_ingredients) > 1:
            break

        elif get_recipe_line.lower() == "xxx" and len(all_ingredients) <2:
            print("You need at least two ingredients in the list.  "
                  "Please add more ingredients.")

        # If exit code is not entered, add ingredient to list
        else:
            all_ingredients.append(get_recipe_line)

    return all_ingredients


def general_converter(how_much, lookup, dictionary, conversion_factor):

    if lookup in dictionary:
        mult_by = dictionary.get(lookup)
        how_much = how_much * float(mult_by) / conversion_factor
        converted = "yes"

    else:
        converted = "no"

    return [how_much, converted]


def unit_checker(raw_unit, us):

    unit_tocheck = raw_unit

    # Abbreviation lists
    teaspoon = ["tsp", "teaspoon", "t", "teaspoons"]
    tablespoon = ["tbs", "tablespoon", "tbsp", "tablespoons"]
    dessertspoon = ["dessertspoon", "dessertspoons"]
    ounce = ["oz", "oz.", "ozs.", "ounce", "fl oz", "ounces"]
    cup = ["c", "cup", "cups"]
    pint = ["p", "pt", "fl pt", "pint", "pints"]
    quart = ["q", "qt", "fl qt", "quart", "quarts"]
    mls = ["ml", "milliliter", "millilitre", "milliliters", "millilitres"]
    litre = ["litre", "liter", "l", "litres", "liters"]
    pound = ["pound", "lb", "lb.", "lbs", "lbs.","#", "pounds"]
    grams = ["g", "gram", "grams"]

    if unit_tocheck == "":
    # print("you chose {}".format(unit_tocheck))
        return unit_tocheck
    elif unit_tocheck.lower() in grams:
        return "g"
    elif unit_tocheck == "T" or unit_tocheck.lower() in tablespoon:
        return "tbs"
    elif unit_tocheck.lower() in teaspoon:
        return "tsp"
    elif unit_tocheck.lower() in dessertspoon:
        return "dessertspoon"
    elif unit_tocheck.lower() in ounce:
        return "ounce"
    elif unit_tocheck.lower() in cup and us == "yes":
        return "us cup"
    elif unit_tocheck.lower() in cup:
        return "cup"
    elif unit_tocheck.lower() in pint:
        return "pint"
    elif unit_tocheck.lower() in quart:
        return "quart"
    elif unit_tocheck.lower() in mls:
        return "ml"
    elif unit_tocheck.lower() in litre:
        return "litre"
    elif unit_tocheck.lower() in pound:
        return "pound"
    else:
        return unit_tocheck

def round_nicely(to_round):

    # round amount appropriately...
    if to_round % 1 == 0:
        to_round = int(to_round)
    elif to_round * 10 % 1 == 0:
        to_round = "{:.1f}".format(to_round)
    else:
        to_round = "{:.2f}".format(to_round)

    return to_round

def instructions():
    print()
    print("******** Instructions ********")
    print()
    print("This program converts recipe ingredients to mls / grams and also allows \n"
          "users to up-size or down-size ingredients.")

    print()
    print("The program will ask for the source of the recipe - we recommend \n"
          "typing in the URL where you found the recipe or the book where \n"
          "it is from so that you can refer back to the original if necessary.")

    print()
    print("The program also asks for the number of servings.  If you are not sure,\n"
          "type '1'.  Then when it asks for servings required, you can increase\n"
          "or decrease the amount (eg 2 or 1/2) and the program will scale the\n"
          "ingredient amounts for you.")
    print()
    print("Note that you can use fractions if needed.  For example, write \n"
          "one half as 1/2 and one and three quarters as 1 3/4")
    print()
    print("Please only type in ONE ingredient per line, if a recipe says \n"
          "'butter or margarine', choose ONE ingredient, either butter \n"
          "or margarine.")
    print()
    print("Lastly, all lines should start with a number / fraction unless \n "
          "no number is give <eg: a pinch of salt>.")
    print()
    print("**********")
    print()

# ***** Main Routine ******
problem = "no"

# set up Dictionaries
unit_central = {
    "tsp": 5,
    "tbs": 15,
    "dessertspoon": 12.5,
    "cup" : 250,
    "us cup": 237,
    "ounce": 28.35,
    "pint": 473,
    "quart": 946,
    "pound": 454,
    "litre": 1000,
    "ml": 1,
    "g": 1
}

# *** Generate food dictionary *****
# open file
groceries = open('01_ingredients_ml_to_g.csv')

# Read data into a list
csv_groceries = csv.reader(groceries)

# Create a dictionary to hold the data
food_dictionary = {}

# Add the data from the list into the dictionary
# (first item in row is key, next is definition)

for row in csv_groceries:
    food_dictionary[row[0]] = row[1]

# set up lists to hold original and 'modernised' recipes
modernised_recipe = []

# ***** Welcome / Instructions ********
print("******** Welcome to the Great Recipe Moderniser ********")
print()

get_instructions = yes_no_check("Welcome.  Is it your first time using this "
                                "program? ")

if get_instructions.lower() == "yes":
    instructions()
else:
    print()

# ******* Get User Input ***********

# Ask user for recipe name and check its not blank
recipe_name = not_blank("What is the recipe name? ",
                   "The recipe name can't be blank and can't contain numbers,",
                   "no")
# Ask user where the recipe is originally from (numbers OK)
source = not_blank("Where is the recipe from? ",
                   "The recipe source can't be blank,",
                   "yes")
print()
us_amounts = yes_no_check("Are you using an American recipe? ")
print()

# Get serving sizes and scale factor
scale_factor = get_sf()
print()

# Get amounts, units and ingredients from user...
full_recipe = get_all_ingredients()

# Split each line of the recipe into amount, unit and ingredient...
mixed_regex = "\d{1,3}\s\d{1,3}\/\d{1,3}"

for recipe_line in full_recipe:
    recipe_line = recipe_line.strip()

    # Get amount...
    if re.match(mixed_regex, recipe_line):

        # Get mixed number by matching the regex
        pre_mixed_num = re.match(mixed_regex, recipe_line)
        mixed_num = pre_mixed_num.group()

        # Replace space with a + sign...
        amount = mixed_num.replace(" ", "+")
        # Change the string into a decimal
        amount = eval(amount)
        amount = amount * scale_factor

        # Get unit and ingredient...
        compile_regex = re.compile(mixed_regex)
        unit_ingredient = re.split(compile_regex, recipe_line)
        unit_ingredient = (unit_ingredient[1]).strip()  # remove extra white space from unit

    else:
        get_amount = recipe_line.split(" ", 1)  # split line at first space

        try:
            # Item has valid amount that is not a mixed fraction
            amount = eval(get_amount[0])    # convert amount to float if possible
            amount = amount * scale_factor

        except NameError:
            # "Pinch of Salt" case (ie: item does not contain concrete amount)
            amount = get_amount[0]
            modernised_recipe.append(recipe_line)
            continue

        except SyntaxError:
            problem = "yes"
            modernised_recipe.append(recipe_line)
            continue

        unit_ingredient = get_amount[1]

    # Get unit and ingredient...
    get_unit = unit_ingredient.split(" ", 1)    # splits text at first space

    num_spaces = recipe_line.count(" ")
    if num_spaces > 1:
        # Item has unit and ingredient
        unit = get_unit[0]
        ingredient = get_unit[1]
        unit = unit_checker(unit, us_amounts)

        # if unit is already in grams, add it to list
        if unit == "g":
            modernised_recipe.append("{:.0f} g {}".format(amount, ingredient))
            continue


        # convert to mls if possible...
        amount = general_converter(amount, unit, unit_central, 1)

        # If we converted to mls, try and convert to grams
        if amount[1] == "yes":
            amount_2 = general_converter(amount[0], ingredient, food_dictionary, 250)

            # if the ingredient is in the list, convert it
            if amount_2[1] == "yes":
                modernised_recipe.append("{:.0f} g {}".format(amount_2[0], ingredient))     # Rather than printing, update modernised list (g)

            # if the ingredient is not in the list, leave the unit as ml
            else:
                modernised_recipe.append("{:.0f} ml {}".format(amount[0], ingredient))
                continue

        # If the unit is not mls, leave the line unchanged
        else:

            # round amount appropriately...
            rounded_amount = round_nicely(amount[0])

            modernised_recipe.append("{} {} {}".format(rounded_amount, unit, ingredient))  # Update list with scaled amount and original unit

    else:
        # Item only has ingredient (no unit)

        rounded_amount = round_nicely(amount)
        modernised_recipe.append("{} {}".format(rounded_amount, unit_ingredient))

# Put updated ingredient in list

# Output ingredient list
print()
print("******** {} Recipe ******".format(recipe_name))
print("Source: {}".format(source))
print()

if problem == "yes":
    print("***** Warning ******")
    print("Some of the entries below might be incorrect as \n"
          "there were problems procesesing some of your inputs.\n"
          "It's possible that you typed a fraction incompletely")
    print()

print("****Ingredients (scaled by a factor of {}) ****".format(scale_factor))
print()
for item in modernised_recipe:
    print(item)