CLI is working, but status needs work: everything is in serving units but I need to know how many bottles or pills to buy

This commit is contained in:
2024-02-03 22:53:49 +01:00
parent 7f2fb0cbbf
commit 6c00e8dd30
13 changed files with 575 additions and 212 deletions

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
# Purpose
To keep track of my vitamins, minerals, supplements and prescriptions so I can know when to order more, and to have enough when I do my monthly 'packaging' of them into easy to use containers.
# Inventory CLI
```bash
> sup status
The next fill-up is on <date>, and you won't have enough of
- x (need quantity x')
- y (need quantity y')
- z (need quantity z')
> sup fill
Okay, next fill-up set to <date X days from now> (configured in `sup.toml`::FILL_EVERY_X_DAYS)
# (this is to add to the inventory; any changes to consumption should be done in `supps.toml`)
> sup add
>> name? <name>
>> date? (today)
>> number of bottles? (1)
>> quantity?
>> serving quantity?
>> serving unit? (mg)
```
# Configuration
TODO: talk about product_aliases
To configure what you take, how much, and when, add entries to `supps.toml`. The fields are configured as such in the `Supp` class:
```python
name: str
units: t.Literal["caps", "mg", "g", "ml", "mcg", "iu"] = "mg"
# these are meant to be doses; units are defined below
morning: int | float = 0
lunch: int | float = 0
dinner: int | float = 0
bedtime: int | float = 0
days_per_week: int = 7
winter_only: bool = False
```

85
main.py
View File

@@ -1,85 +0,0 @@
import collections
from dataclasses import dataclass
import json
import pathlib as pl
import tomllib
import typing as t
from rich.pretty import pprint as print
def load_config():
return tomllib.loads(pl.Path("supps.toml").read_text())
def load_ordered_supps():
return json.loads(pl.Path('supps.json').read_text())
ALIASES = {
'Naturally Sourced Vitamin E': "vitamin e",
"Natural Vitamin K2 MK-7 with MenaQ7": "k2-mk7",
"Liquid D-3 & MK-7": "d-3",
"Calcium AKG Longevity": "ca-akg",
"MK-7 Vitamin K-2": "k2-mk7",
"Optimized Folate": "methyl folate",
"Extend-Release Magnesium": "magnesium slow release",
"Crucera-SGS": "broccomax",
"B-50": "b complex",
"Vitamin K2, MK-4 (Menatetrenone)": "k2-mk4",
"Super K": "k1",
"Ultimate Omega, Lemon": "epa/dha",
}
ALIASES_REV = {v: k for k, v in ALIASES.items()}
class Missing(Exception):
pass
def validate_matches() -> None:
missing = []
config = load_config()
ordered_supp_names_lower = [i['name'].lower() for i in load_ordered_supps()]
for i in config['supps']:
if (
not any(i['name'].lower() in ordered_supp_name for ordered_supp_name in ordered_supp_names_lower)
and i['name'].lower() not in ALIASES_REV
):
missing.append(i['name'])
if missing:
raise Missing(', '.join(missing))
@dataclass
class Supp:
name: str
morning: int | float = 0
lunch: int | float = 0
dinner: int | float = 0
bedtime: int | float = 0
per_week: int = 7
units: t.Literal["caps", "mg", "g", "ml", "mcg", "iu"] = "mg"
winter_only: bool = False
# special case: Liquid D-3 & MK-7 -- has d-3 and k-mk7
# special case: K Complex has K1, MK-4 and MK-7 in it
"""
{'orderDate': '2024-01-04T23:00:00.000Z',
'name': 'Naturally Sourced Vitamin E',
'quantity': 100,
'quantityUnits': 'caps',
'servingUnit': 'mg',
'numUnitsInServing': 134,
'numBottles': 2}]
"""
missing_names = validate_matches()
if missing_names:
print(missing_names)

142
poetry.lock generated Normal file
View File

@@ -0,0 +1,142 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "click"
version = "8.1.7"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "pygments"
version = "2.17.2"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.7"
files = [
{file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
{file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
]
[package.extras]
plugins = ["importlib-metadata"]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "rich"
version = "13.7.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"},
{file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
[[package]]
name = "typer"
version = "0.9.0"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
python-versions = ">=3.6"
files = [
{file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"},
{file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"},
]
[package.dependencies]
click = ">=7.1.1,<9.0.0"
typing-extensions = ">=3.7.4.3"
[package.extras]
all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"]
dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"]
doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"]
test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"]
[[package]]
name = "typing-extensions"
version = "4.9.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "4760c0a423126e044bb2d35b8bff39a0c605f92514ca863afbd72930d00fa619"

20
pyproject.toml Normal file
View File

@@ -0,0 +1,20 @@
[tool.poetry]
name = "sup"
version = "0.1.0"
description = ""
authors = ["Zev Averbach <zev@averba.ch>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
rich = "^13.7.0"
typer = "^0.9.0"
toml = "^0.10.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
sup = "sup.cli:app"

0
src/sup/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

138
src/sup/cli.py Normal file
View File

@@ -0,0 +1,138 @@
"""
{'orderDate': '2024-01-04T23:00:00.000Z',
'name': 'Naturally Sourced Vitamin E',
'quantity': 100,
'quantityUnits': 'caps',
'servingUnit': 'mg',
'numUnitsInServing': 134,
'numBottles': 2}]
"""
import datetime as dt
import json
import toml
from typing_extensions import Annotated
import typer
from sup.main import load_ordered_supps, ORDERED_SUPPS_FP, load_config, SUPP_CONSUMPTION_FP, load_inventory, Supp
app = typer.Typer()
class UnitMismatch(Exception):
pass
def get_qty_inventory(supp: Supp, inventory: dict) -> int:
inventory_order_date = dt.datetime.strptime(inventory['orderDate'][:10], "%Y-%m-%d").date()
num_days_since_bought = (dt.date.today() - inventory_order_date).days
if inventory['servingUnit'] != supp.units:
raise UnitMismatch(inventory, supp)
return (inventory['quantity'] * inventory['numBottles']) - (supp * num_days_since_bought)
def get_num_winter_days_starting(num_days: int, starting: dt.date) -> int:
# dec 21 to mar 20
winter_starts = dt.date(starting.year, 12, 21)
if starting <= winter_starts:
num_days_til_winter = (winter_starts - starting).days
if num_days_til_winter > num_days:
return 0
return num_days - num_days_til_winter
winter_ends = dt.date(starting.year, 3, 2)
if starting >= winter_ends:
winter_starts = dt.date(starting.year + 1, 12, 21)
num_days_til_winter = (winter_starts - starting).days
if num_days_til_winter > num_days:
return 0
return num_days - num_days_til_winter
winter_ends = dt.date(starting.year + 1, 3, 2)
if starting <= winter_ends:
num_days_til_winter_ends = (winter_ends - starting).days
if num_days_til_winter_ends > num_days:
return num_days
return num_days_til_winter_ends
raise Exception
@app.command()
def status():
"""check if there's enough inventory for the next fill-up; if not, what to order?"""
config = load_config()
num_days_of_inventory_needed = config['FILL_EVERY_X_DAYS']
next_fill_date = config['LAST_FILL_DATE'] + dt.timedelta(num_days_of_inventory_needed)
inventory = load_inventory()
needs = []
for sup in config['supps']:
sup_inst = Supp(**sup)
if sup_inst.winter_only:
num_days_of_inventory_needed = get_num_winter_days_starting(num_days_of_inventory_needed, next_fill_date)
qty_needed = sup_inst * num_days_of_inventory_needed
try:
inv = inventory[sup_inst.name]
except KeyError:
qty_of_inventory = 0
else:
qty_of_inventory = get_qty_inventory(sup_inst, inv)
net_need = qty_needed - qty_of_inventory
if net_need > 0:
needs.append((sup_inst.name, net_need, sup_inst.units))
if needs:
print(f"The next fill-up is on {next_fill_date}, and you won't have enough of:")
for name, quant, units in needs:
print(f"{name} (need {quant} {units})")
@app.command()
def fill():
"""reset 'next fill' clock"""
config = load_config()
today = dt.date.today()
config["LAST_FILL_DATE"] = today.strftime("%Y-%m-%d")
# TODO: make sure this toml library doesn't add quotes to this entry, it
# makes it so when reading it doesn't get parsed to a date
fill_every_x_days = config['FILL_EVERY_X_DAYS']
save_config(config)
print(f"Okay, next fill-up set to {today + dt.timedelta(days=fill_every_x_days)} (configured in `sup.toml`::FILL_EVERY_X_DAYS)")
@app.command()
def add(
name: Annotated[str, typer.Option(prompt=True)],
quantity: Annotated[int, typer.Option(prompt=True)],
serving_quantity: Annotated[int, typer.Option(prompt=True)],
serving_unit: Annotated[str, typer.Option(prompt=True)] = "mg",
quantity_unit: Annotated[str, typer.Option(prompt=True)] = "caps",
date: Annotated[dt.datetime, typer.Option(help="(today)", prompt=True)]
| None = None,
number_of_bottles: Annotated[int, typer.Option(prompt=True)] = 1,
) -> None:
"""add to inventory"""
if date is None:
date = dt.datetime.now().date() # type: ignore
else:
date = date.date() # type: ignore
ordered_supps = load_ordered_supps()
order_dict = dict(
name=name,
quantity=quantity,
numUnitsInServing=serving_quantity,
servingUnit=serving_unit,
quantityUnit=quantity_unit,
orderDate=date.strftime("%Y-%m-%d"), # type: ignore
numBottles=number_of_bottles,
)
ordered_supps.append(order_dict)
save_ordered_supps(ordered_supps)
print(f"added {order_dict} to {ORDERED_SUPPS_FP}")
def save_ordered_supps(ordered_supps: list[dict]) -> None:
ORDERED_SUPPS_FP.write_text(json.dumps(ordered_supps, indent=2))
def save_config(config: dict) -> None:
SUPP_CONSUMPTION_FP.write_text(toml.dumps(config))

90
src/sup/main.py Normal file
View File

@@ -0,0 +1,90 @@
"""
TODO:
special case: Liquid D-3 & MK-7 -- has d-3 and k-mk7
special case: K Complex has K1, MK-4 and MK-7 in it
"""
from dataclasses import dataclass
import json
import pathlib as pl
import tomllib
import typing as t
ORDERED_SUPPS_FP = pl.Path("supps.json")
SUPP_CONSUMPTION_FP = pl.Path("supps.toml")
def load_config():
return tomllib.loads(SUPP_CONSUMPTION_FP.read_text())
def load_ordered_supps() -> list[dict]:
return json.loads(ORDERED_SUPPS_FP.read_text())
CONFIG = load_config()
ALIASES = CONFIG["product_aliases"]
ordered_supps = load_ordered_supps()
for i in CONFIG["supps"]:
for ordered_supp in ordered_supps:
if i["name"].lower() in ordered_supp['name'].lower():
ALIASES[ordered_supp['name']] = i["name"]
ALIASES_REV = {v: k for k, v in ALIASES.items()}
def load_inventory():
return { ALIASES[s['name']]: s for s in load_ordered_supps() if s['name'] not in CONFIG['discontinued'] }
class Missing(Exception):
pass
def validate_matches() -> None:
missing = []
ordered_supp_names_lower = [i["name"].lower() for i in load_ordered_supps()]
for i in CONFIG["supps"]:
if (
not any(
i["name"].lower() in ordered_supp_name
for ordered_supp_name in ordered_supp_names_lower
)
and i["name"].lower() not in ALIASES_REV
):
missing.append(i["name"])
if missing:
raise Missing(", ".join(missing))
@dataclass
class Supp:
name: str
morning: int | float = 0
lunch: int | float = 0
dinner: int | float = 0
bedtime: int | float = 0
days_per_week: int = 7
units: t.Literal["caps", "mg", "g", "ml", "mcg", "iu"] = "mg"
winter_only: bool = False
def __mul__(self, other: int) -> float:
return self.quantity_per_day * other
@property
def quantity_per_day(self) -> float:
return sum([self.morning, self.lunch, self.dinner, self.bedtime]) * 7 / self.days_per_week
def main():
missing_names = validate_matches()
if missing_names:
print(missing_names)
if __name__ == "__main__":
main()

View File

@@ -13,8 +13,8 @@
"name": "Super K",
"quantity": 90,
"quantityUnits": "caps",
"servingUnit": "caps",
"numUnitsInServing": 1,
"servingUnit": "mcg",
"numUnitsInServing": 1500,
"numBottles": 1
},
{
@@ -58,8 +58,8 @@
"name": "Glycine",
"quantity": 250,
"quantityUnits": "caps",
"servingUnit": "caps",
"numUnitsInServing": 1,
"servingUnit": "mg",
"numUnitsInServing": 350,
"numBottles": 1
},
{
@@ -83,11 +83,11 @@
{
"orderDate": "2024-01-23T23:00:00.000Z",
"name": "Liquid D-3 & MK-7",
"quantity": 30,
"quantityUnits": "ml",
"servingUnit": "ml",
"numUnitsInServing": 0.368,
"numBottles": 4
"quantity": 81.5,
"quantityUnits": "iu",
"servingUnit": "iu",
"numUnitsInServing": 5000,
"numBottles": 1
},
{
"orderDate": "2024-01-23T23:00:00.000Z",
@@ -112,7 +112,7 @@
"name": "Magnesium Taurate",
"quantity": 180,
"quantityUnits": "caps",
"servingUnit": "cap",
"servingUnit": "caps",
"numUnitsInServing": 1,
"numBottles": 1
},
@@ -184,8 +184,8 @@
"name": "Probiotic-10",
"quantity": 50,
"quantityUnits": "caps",
"servingUnit": "Billion",
"numUnitsInServing": 25,
"servingUnit": "caps",
"numUnitsInServing": 1,
"numBottles": 1
},
{
@@ -193,8 +193,8 @@
"name": "Iron Bisglycinate",
"quantity": 60,
"quantityUnits": "caps",
"servingUnit": "cap",
"numUnitsInServing": 1,
"servingUnit": "mg",
"numUnitsInServing": 25,
"numBottles": 1
},
{
@@ -211,7 +211,7 @@
"name": "Extend-Release Magnesium",
"quantity": 60,
"quantityUnits": "caps",
"servingUnit": "cap",
"servingUnit": "caps",
"numUnitsInServing": 1,
"numBottles": 1
},
@@ -231,7 +231,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 125,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -240,16 +240,16 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 600,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
"name": "Crucera-SGS",
"quantity": 60,
"quantityUnits": "caps",
"servingUnit": "cap",
"servingUnit": "caps",
"numUnitsInServing": 1,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -258,7 +258,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 1000,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -267,7 +267,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 300,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -276,7 +276,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 12,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -285,7 +285,7 @@
"quantityUnits": "caps",
"servingUnit": "mcg",
"numUnitsInServing": 1000,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -294,7 +294,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 3,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -303,7 +303,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 1000,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -312,16 +312,16 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 2250,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
"name": "Lutein & Zeaxanthin",
"quantity": 60,
"quantityUnits": "caps",
"servingUnit": "cap",
"numUnitsInServing": 1,
"numBottles": 2
"servingUnit": "mg",
"numUnitsInServing": 20,
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -330,7 +330,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 15,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -339,7 +339,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 300,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -348,7 +348,7 @@
"quantityUnits": "g",
"servingUnit": "g",
"numUnitsInServing": 5,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -357,7 +357,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 10,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -366,7 +366,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 500,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -375,7 +375,7 @@
"quantityUnits": "caps",
"servingUnit": "mcg",
"numUnitsInServing": 100,
"numBottles": 2
"numBottles": 4
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -384,7 +384,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 1000,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -393,25 +393,25 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 500,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
"name": "Lithium Orotate Drops",
"quantity": 59,
"quantityUnits": "ml",
"servingUnit": "ml",
"numUnitsInServing": 0.25,
"numBottles": 2
"servingUnit": "mg",
"numUnitsInServing": 1,
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
"name": "Liquid Iodine Plus",
"quantity": 59,
"quantityUnits": "ml",
"servingUnit": "ml",
"numUnitsInServing": 0.126,
"numBottles": 2
"servingUnit": "mcg",
"numUnitsInServing": 125,
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -420,7 +420,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 100,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -429,7 +429,7 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 81,
"numBottles": 2
"numBottles": 1
},
{
"orderDate": "2024-01-04T23:00:00.000Z",
@@ -438,6 +438,6 @@
"quantityUnits": "caps",
"servingUnit": "mg",
"numUnitsInServing": 134,
"numBottles": 2
"numBottles": 1
}
]

View File

@@ -1,207 +1,217 @@
[[ supps ]]
discontinued = ['NAD+ Cell Regenerator', 'Fisetin with Novusetin', 'Fisetin Novusetin', 'Collagen']
FILL_EVERY_X_DAYS = 30
LAST_FILL_DATE = 2024-01-16
[[supps]]
name = "ashwagandha"
bedtime = 600
[[ supps ]]
[[supps]]
name = "aspirin"
lunch = 86
per_week = 3
days_per_week = 3
[[ supps ]]
[[supps]]
name = "astaxanthin"
morning = 12
[[ supps ]]
[[supps]]
name = "b complex"
morning = 0.5
per_week = 2
days_per_week = 2
units = "caps"
[[ supps ]]
[[supps]]
name = "b-12"
lunch = 1000
units = "mcg"
per_week = 1
days_per_week = 1
[[ supps ]]
[[supps]]
name = "boron"
morning = 2
[[ supps ]]
[[supps]]
name = "broccomax"
morning = 17.5
dinner = 17.5
morning = 1
dinner = 1
units = "caps"
[[ supps ]]
[[supps]]
name = "vitamin c"
morning = 500
[[ supps ]]
[[supps]]
name = "creatine"
lunch = 2.5
days_per_week = 4
units = "g"
per_week = 4
[[ supps ]]
[[supps]]
name = "Ca-AKG"
morning = 1
dinner = 1
units = "g"
morning = 1000
dinner = 1000
[[ supps ]]
[[supps]]
name = "coq10"
morning = 100
lunch = 100
dinner = 100
[[ supps ]]
[[supps]]
name = "d-3"
morning = 5000
units = "iu"
winter_only = true
[[ supps ]]
[[supps]]
name = "DHEA"
morning = 25
[[ supps ]]
[[supps]]
name = "vitamin e"
morning = 200
per_week = 3
units = "iu"
morning = 67
days_per_week = 3
[[ supps ]]
[[supps]]
name = "EPA/DHA"
morning = 2
dinner = 1
units = "caps"
[[ supps ]]
[[supps]]
name = "garlic"
morning = 1.2
dinner = 1.2
units = "g"
morning = 1200
dinner = 1200
[[ supps ]]
[[supps]]
name = "genistein"
morning = 1
units = "caps"
morning = 125
[[ supps ]]
[[supps]]
name = "ginger root"
morning = 2.2
dinner = 2.2
units = "g"
morning = 2200
dinner = 2200
[[ supps ]]
[[supps]]
name = "glucosamine sulfate"
morning = 1500
dinner = 1500
[[ supps ]]
[[supps]]
name = "glycine"
morning = 2
units = "g"
morning = 2000
[[ supps ]]
[[supps]]
name = "hyaluronic acid"
morning = 300
[[ supps ]]
[[supps]]
name = "iodine"
morning = 125
units = "mcg"
[[ supps ]]
[[supps]]
name = "iron"
morning = 10
[[ supps ]]
[[supps]]
name = "k1"
morning = 1.5
morning = 1500
units="mcg"
[[ supps ]]
[[supps]]
name = "k2-mk7"
morning = 600
units = "mcg"
[[ supps ]]
[[supps]]
name = "k2-mk4"
morning = 5
[[ supps ]]
[[supps]]
name = "l-lysine"
morning = 1
dinner = 1
units = "g"
morning = 1000
dinner = 1000
[[ supps ]]
[[supps]]
name = "l-tyrosine"
dinner = 500
[[ supps ]]
[[supps]]
name = "lithium orotate"
morning = 1
[[ supps ]]
[[supps]]
name = "lycopene"
morning = 10
[[ supps ]]
[[supps]]
name = "magnesium slow release"
morning = 1
units = "caps"
[[ supps ]]
[[supps]]
name = "magnesium taurate"
bedtime = 125
[[ supps ]]
[[supps]]
name = "magnesium glycinate"
bedtime = 350
[[ supps ]]
[[supps]]
name = "magtein"
bedtime = 2
units = "g"
bedtime = 2000
[[ supps ]]
[[supps]]
name = "methyl folate"
morning = 1
units = "caps"
morning = 1700
units = "mcg"
[[ supps ]]
[[supps]]
name = "NAC"
morning = 1800
dinner = 1800
[[ supps ]]
[[supps]]
name = "pqq"
morning = 1
units = "caps"
morning = 20
[[ supps ]]
[[supps]]
name = "probiotic"
morning = 1
units = "caps"
[[ supps ]]
[[supps]]
name = "taurine"
morning = 2
dinner = 1
units = "g"
morning = 2000
dinner = 1000
[[ supps ]]
[[supps]]
name = "turmeric"
morning = 1
dinner = 1
units = "g"
morning = 1000
dinner = 1000
[[ supps ]]
[[supps]]
name = "zeaxanthin"
morning = 20
per_week = 3
days_per_week = 3
[[ supps ]]
[[supps]]
name = "zinc"
morning = 15
[product_aliases]
"Naturally Sourced Vitamin E" = "vitamin e"
"Natural Vitamin K2 MK-7 with MenaQ7" = "k2-mk7"
"Liquid D-3 & MK-7" = "d-3"
"Calcium AKG Longevity" = "ca-akg"
"MK-7 Vitamin K-2" = "k2-mk7"
"Optimized Folate" = "methyl folate"
"Extend-Release Magnesium" = "magnesium slow release"
Crucera-SGS = "broccomax"
B-50 = "b complex"
"Vitamin K2, MK-4 (Menatetrenone)" = "k2-mk4"
"Super K" = "k1"
"Ultimate Omega, Lemon" = "epa/dha"