Metadata-Version: 2.5
Name: pantryfold
Version: 0.1.0
Summary: Merge grocery quantities, scale servings, and subtract pantry stock.
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# pantryfold

Combine ingredient quantities into one shopping list, then subtract what you
already have. No AI, API keys or runtime dependencies. Python 3.10+. MIT licensed.

## Install

~~~console
python -m pip install pantryfold
~~~

Use **python -m pip install .** for a local checkout.
The CLI also works as **python -m pantryfold**.

## Python

~~~python
from pantryfold import Ingredient, shopping_list, format_quantity

required = [Ingredient("milk", 500, "ml"), Ingredient("Milk", 1, "l")]
stock = [Ingredient("milk", 250, "ml")]
items = shopping_list(required, pantry=stock)

assert items == [Ingredient("milk", 1250, "ml")]
assert format_quantity(items[0].quantity) == "1250"
~~~

## Command line

Create recipe-a.json:

~~~json
[
  {"name": "milk", "quantity": "500", "unit": "ml"},
  {"name": "eggs", "quantity": "2", "unit": "pcs"}
]
~~~

Create recipe-b.json with the other recipe and pantry.json with items already
on hand, using the same format.

~~~console
pantryfold recipe-a.json recipe-b.json --pantry pantry.json
pantryfold recipe-a.json --scale 2 --format markdown
pantryfold recipe-a.json --aliases aliases.json
~~~

Output is JSON by default. Markdown output is a checkbox shopping list.
Input files are UTF-8; one input may be a dash to read standard input. Every
entry must contain exactly name, quantity and unit. Misspelled fields are
rejected. Quantity strings support decimals or fractions such as "1/3".

## Matching and units

- Names are case-folded, Unicode NFC-normalized, trimmed, and whitespace is
  collapsed. The normalized name is used in output.
- Different names are merged only through explicit aliases. For example,
  aliases.json may contain {"scallion": "green onion"}. Chains are supported;
  cycles and conflicting aliases are rejected.
- Weight: g and kg. Volume: ml and l. Count: pcs. Common English spellings are
  accepted, as are 克, 千克, 公斤, 毫升, 升, 个.
- Results use canonical g, ml and pcs units, in first-appearance order.
- Weight and volume are never converted into each other. Milk in g and milk
  in ml therefore remain separate entries. Cups, teaspoons, ingredient density,
  nutritional calculations and free-form recipe parsing are outside this release.
- Requirements are merged and scaled FIRST; stock is subtracted once afterward.
  Surplus stock and zero requirements are omitted from the shopping list.
  Fractional counts are permitted; quantities are not rounded to shopping packs.

**Ingredient(name, quantity, unit)** stores an exact Fraction quantity.
**shopping_list(ingredients, *, pantry=(), scale=1, aliases=None)** returns a
list of Ingredient objects. **format_quantity()** writes terminating decimals
exactly and leaves other rational values as fractions.

CLI quantities are strings to preserve exact values and can be read back as
input. Original files are not modified. Exit status: 0 on success, 2 on invalid
data or unreadable files.

## Development

~~~console
python -m pip install -e .
python -m unittest discover -s tests -v
~~~
