Skip to content

Instantly share code, notes, and snippets.

@ahawker
Last active July 27, 2023 23:30
Show Gist options
  • Save ahawker/3cb6641308e7a578e2a38fc0f3008f17 to your computer and use it in GitHub Desktop.
Save ahawker/3cb6641308e7a578e2a38fc0f3008f17 to your computer and use it in GitHub Desktop.
ingredient-selector

Ingredient Selector

Perform a random selection (weighted) of ingredients from the data files.

Development

Update the ingredient files and run! The script will automatically decrement the weights of its selections to promote new items.

Usage

❯ python3 ingredient_selector.py --help
usage: ingredient_selector [-h] [-k K] [-u]

Randomly select ingredients from dataset files for #club-cooking challenge!

optional arguments:
  -h, --help    show this help message and exit
  -k K          Number of random values to return.
  -u, --update  Flag indicating if ingredient weights should be updated.

Example

$ chmod u+x ingredient_selector.py

# Test it out with a random selection.
❯ python3 ingredient_selector.py
fruit.csv: Selected orange at weight 100
vegetable.csv: Selected tomatillo at weight 100
protein.csv: Selected lamb at weight 100

# Make a legit selection and update the weights.
❯ python3 ingredient_selector.py --update
fruit.csv: Selected pear at weight 50
fruit.csv: Updated pear to weight 25

vegetable.csv: Selected asparagas at weight 12
vegetable.csv: Updated asparagas to weight 6

protein.csv: Selected beef at weight 12
protein.csv: Updated beef to weight 6
name weight
almond 100
apple 100
apriot 100
banana 100
blackberry 100
blueberry 100
cantaloupe 100
cherry 100
coconut 100
coffee 100
cranberry 100
currant 100
date 100
elderberry 100
fig 100
grape 100
grapefruit 100
guava 100
kiwi 100
lemon 100
lime 100
mango 100
nectarine 100
nutmeg 100
orange 100
passionfruit 100
pineapple 100
plum 100
peach 100
pear 100
raspberry 100
strawberry 100
yucca 100
watermelon 100
#!/usr/bin/env python3
import argparse
import collections
import csv
import dataclasses
import glob
import io
import random
# List of ingredient files to use.
INGREDIENT_FILES = glob.glob('*.csv')
# Entry represents an individual row from an ingredient file.
@dataclasses.dataclass
class Entry:
name: str = ""
weight: str = ""
# Field names for the ingredient files.
FIELDS = ['name', 'weight']
# Returns a new weight for the entry if selected.
def new_weight(entry):
return entry.weight // 2
# Generator func that yields Entry instances for all entries in the ingredient file.
def read_csv(path):
with io.open(path, 'r') as csvfile:
for row in csv.DictReader(csvfile):
# print(row)
yield Entry(**row)
# Write ingredient data back to a file. For the entry that was selected
# in this round, we'll store its new weight value to reduce likelihood of it being
# selected soon.
def update_entry_weight_from(path, entries, selection, selection_weight):
with io.open(path, 'w') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=FIELDS)
writer.writeheader()
for entry in entries:
if entry.name == selection.name:
entry.weight = selection_weight
writer.writerow(dict(name=entry.name, weight=entry.weight))
# Load ingredient file and make a (weighted) random selection of its entries.
def random_selection_from(entries, k):
population = [e.name for e in entries]
weights = [int(e.weight) for e in entries]
for name in random.choices(population, weights, k=k):
yield Entry(name, weights[population.index(name)])
def main():
parser = argparse.ArgumentParser(
prog='ingredient_selector',
description='Randomly select ingredients from dataset files for #club-cooking challenge!',
)
parser.add_argument(
'-k',
action='store',
default=1,
type=int,
help='Number of random values to return.'
)
parser.add_argument(
'-u',
'--update',
action='store_true',
help='Flag indicating if ingredient weights should be updated.'
)
args = parser.parse_args()
for path in INGREDIENT_FILES:
entries = list(read_csv(path))
for entry in random_selection_from(entries, args.k):
print(f'{path}: Selected {entry.name} at weight {entry.weight}')
if args.update:
entry_weight = new_weight(entry)
update_entry_weight_from(path, entries, entry, entry_weight)
print(f'{path}: Updated {entry.name} to weight {entry_weight}')
print()
if __name__ == '__main__':
main()
name weight
beef 100
bivalve (mussels/oysters/scallops/clams) 100
chicken 100
crustacean (crabs/lobsters/shrimps) 100
duck 100
flat fish (halibut/sole/skate) 100
lamb 100
oily fish (anchovy/mackerel/herring/tuna/swordfish) 100
pork 100
rabbit 100
salmon 100
turkey 100
white fish (snapper/cod/bass) 100
name weight
black beans 100
black eyed peas 100
edamame 100
eggs 100
garbanzo beans (chickpeas) 100
lentils 100
lima beans 100
mung beans 100
navy beans 100
peas 100
pinto beans 100
plant based meat 100
seitan 100
soy beans 100
tofu 100
tofurky 100
white beans 100
name weight
artichoke 100
asparagas 100
avocado 100
bamboo shoots 100
basil 100
beet 100
bok choy 100
broccoli 100
brussels sprouts 100
cabbage 100
carrot 100
cauliflower 100
celery 100
celery root 100
chard 100
corn 100
cucumber 100
daikon 100
elderberry 100
eggplant 100
fennel 100
garlic 100
green bean 100
collard greens 100
mustard greens 100
jicama 100
kale 100
kohlrabi 100
leeks 100
lettuce 100
lima beans 100
mushrooms 100
okra 100
onion 100
parsnips 100
spicy peppers 100
sweet peppers 100
potatoes 100
pumpkin 100
radicchio 100
radish 100
rhubarb 100
romanesco 100
rutabaga 100
seaweed 100
shallots 100
spinach 100
squash 100
sweet potato 100
tomatillo 100
tomatoes 100
turnips 100
water chestnuts 100
yams 100
zucchini 100
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment