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:
48
README.md
Normal file
48
README.md
Normal 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
85
main.py
@@ -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
142
poetry.lock
generated
Normal 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
20
pyproject.toml
Normal 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
0
src/sup/__init__.py
Normal file
BIN
src/sup/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
src/sup/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/sup/__pycache__/cli.cpython-312.pyc
Normal file
BIN
src/sup/__pycache__/cli.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/sup/__pycache__/main.cpython-312.pyc
Normal file
BIN
src/sup/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
138
src/sup/cli.py
Normal file
138
src/sup/cli.py
Normal 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
90
src/sup/main.py
Normal 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()
|
||||||
92
supps.json
92
supps.json
@@ -13,8 +13,8 @@
|
|||||||
"name": "Super K",
|
"name": "Super K",
|
||||||
"quantity": 90,
|
"quantity": 90,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "caps",
|
"servingUnit": "mcg",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 1500,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -58,8 +58,8 @@
|
|||||||
"name": "Glycine",
|
"name": "Glycine",
|
||||||
"quantity": 250,
|
"quantity": 250,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "caps",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 350,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -83,11 +83,11 @@
|
|||||||
{
|
{
|
||||||
"orderDate": "2024-01-23T23:00:00.000Z",
|
"orderDate": "2024-01-23T23:00:00.000Z",
|
||||||
"name": "Liquid D-3 & MK-7",
|
"name": "Liquid D-3 & MK-7",
|
||||||
"quantity": 30,
|
"quantity": 81.5,
|
||||||
"quantityUnits": "ml",
|
"quantityUnits": "iu",
|
||||||
"servingUnit": "ml",
|
"servingUnit": "iu",
|
||||||
"numUnitsInServing": 0.368,
|
"numUnitsInServing": 5000,
|
||||||
"numBottles": 4
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-23T23:00:00.000Z",
|
"orderDate": "2024-01-23T23:00:00.000Z",
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
"name": "Magnesium Taurate",
|
"name": "Magnesium Taurate",
|
||||||
"quantity": 180,
|
"quantity": 180,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "cap",
|
"servingUnit": "caps",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 1,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
@@ -184,8 +184,8 @@
|
|||||||
"name": "Probiotic-10",
|
"name": "Probiotic-10",
|
||||||
"quantity": 50,
|
"quantity": 50,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "Billion",
|
"servingUnit": "caps",
|
||||||
"numUnitsInServing": 25,
|
"numUnitsInServing": 1,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -193,8 +193,8 @@
|
|||||||
"name": "Iron Bisglycinate",
|
"name": "Iron Bisglycinate",
|
||||||
"quantity": 60,
|
"quantity": 60,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "cap",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 25,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
"name": "Extend-Release Magnesium",
|
"name": "Extend-Release Magnesium",
|
||||||
"quantity": 60,
|
"quantity": 60,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "cap",
|
"servingUnit": "caps",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 1,
|
||||||
"numBottles": 1
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 125,
|
"numUnitsInServing": 125,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -240,16 +240,16 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 600,
|
"numUnitsInServing": 600,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
"name": "Crucera-SGS",
|
"name": "Crucera-SGS",
|
||||||
"quantity": 60,
|
"quantity": 60,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "cap",
|
"servingUnit": "caps",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 1,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1000,
|
"numUnitsInServing": 1000,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 300,
|
"numUnitsInServing": 300,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -276,7 +276,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 12,
|
"numUnitsInServing": 12,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -285,7 +285,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mcg",
|
"servingUnit": "mcg",
|
||||||
"numUnitsInServing": 1000,
|
"numUnitsInServing": 1000,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 3,
|
"numUnitsInServing": 3,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -303,7 +303,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1000,
|
"numUnitsInServing": 1000,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -312,16 +312,16 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 2250,
|
"numUnitsInServing": 2250,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
"name": "Lutein & Zeaxanthin",
|
"name": "Lutein & Zeaxanthin",
|
||||||
"quantity": 60,
|
"quantity": 60,
|
||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "cap",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1,
|
"numUnitsInServing": 20,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -330,7 +330,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 15,
|
"numUnitsInServing": 15,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -339,7 +339,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 300,
|
"numUnitsInServing": 300,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
"quantityUnits": "g",
|
"quantityUnits": "g",
|
||||||
"servingUnit": "g",
|
"servingUnit": "g",
|
||||||
"numUnitsInServing": 5,
|
"numUnitsInServing": 5,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -357,7 +357,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 10,
|
"numUnitsInServing": 10,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -366,7 +366,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 500,
|
"numUnitsInServing": 500,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -375,7 +375,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mcg",
|
"servingUnit": "mcg",
|
||||||
"numUnitsInServing": 100,
|
"numUnitsInServing": 100,
|
||||||
"numBottles": 2
|
"numBottles": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -384,7 +384,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 1000,
|
"numUnitsInServing": 1000,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -393,25 +393,25 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 500,
|
"numUnitsInServing": 500,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
"name": "Lithium Orotate Drops",
|
"name": "Lithium Orotate Drops",
|
||||||
"quantity": 59,
|
"quantity": 59,
|
||||||
"quantityUnits": "ml",
|
"quantityUnits": "ml",
|
||||||
"servingUnit": "ml",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 0.25,
|
"numUnitsInServing": 1,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
"name": "Liquid Iodine Plus",
|
"name": "Liquid Iodine Plus",
|
||||||
"quantity": 59,
|
"quantity": 59,
|
||||||
"quantityUnits": "ml",
|
"quantityUnits": "ml",
|
||||||
"servingUnit": "ml",
|
"servingUnit": "mcg",
|
||||||
"numUnitsInServing": 0.126,
|
"numUnitsInServing": 125,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -420,7 +420,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 100,
|
"numUnitsInServing": 100,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -429,7 +429,7 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 81,
|
"numUnitsInServing": 81,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"orderDate": "2024-01-04T23:00:00.000Z",
|
"orderDate": "2024-01-04T23:00:00.000Z",
|
||||||
@@ -438,6 +438,6 @@
|
|||||||
"quantityUnits": "caps",
|
"quantityUnits": "caps",
|
||||||
"servingUnit": "mg",
|
"servingUnit": "mg",
|
||||||
"numUnitsInServing": 134,
|
"numUnitsInServing": 134,
|
||||||
"numBottles": 2
|
"numBottles": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
172
supps.toml
172
supps.toml
@@ -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"
|
name = "ashwagandha"
|
||||||
bedtime = 600
|
bedtime = 600
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "aspirin"
|
name = "aspirin"
|
||||||
lunch = 86
|
lunch = 86
|
||||||
per_week = 3
|
days_per_week = 3
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "astaxanthin"
|
name = "astaxanthin"
|
||||||
morning = 12
|
morning = 12
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "b complex"
|
name = "b complex"
|
||||||
morning = 0.5
|
morning = 0.5
|
||||||
per_week = 2
|
days_per_week = 2
|
||||||
units = "caps"
|
units = "caps"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "b-12"
|
name = "b-12"
|
||||||
lunch = 1000
|
lunch = 1000
|
||||||
units = "mcg"
|
units = "mcg"
|
||||||
per_week = 1
|
days_per_week = 1
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "boron"
|
name = "boron"
|
||||||
morning = 2
|
morning = 2
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "broccomax"
|
name = "broccomax"
|
||||||
morning = 17.5
|
morning = 1
|
||||||
dinner = 17.5
|
dinner = 1
|
||||||
|
units = "caps"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "vitamin c"
|
name = "vitamin c"
|
||||||
morning = 500
|
morning = 500
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "creatine"
|
name = "creatine"
|
||||||
lunch = 2.5
|
lunch = 2.5
|
||||||
|
days_per_week = 4
|
||||||
units = "g"
|
units = "g"
|
||||||
per_week = 4
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "Ca-AKG"
|
name = "Ca-AKG"
|
||||||
morning = 1
|
morning = 1000
|
||||||
dinner = 1
|
dinner = 1000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "coq10"
|
name = "coq10"
|
||||||
morning = 100
|
morning = 100
|
||||||
lunch = 100
|
lunch = 100
|
||||||
dinner = 100
|
dinner = 100
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "d-3"
|
name = "d-3"
|
||||||
morning = 5000
|
morning = 5000
|
||||||
units = "iu"
|
units = "iu"
|
||||||
winter_only = true
|
winter_only = true
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "DHEA"
|
name = "DHEA"
|
||||||
morning = 25
|
morning = 25
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "vitamin e"
|
name = "vitamin e"
|
||||||
morning = 200
|
morning = 67
|
||||||
per_week = 3
|
days_per_week = 3
|
||||||
units = "iu"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "EPA/DHA"
|
name = "EPA/DHA"
|
||||||
morning = 2
|
morning = 2
|
||||||
dinner = 1
|
dinner = 1
|
||||||
units = "caps"
|
units = "caps"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "garlic"
|
name = "garlic"
|
||||||
morning = 1.2
|
morning = 1200
|
||||||
dinner = 1.2
|
dinner = 1200
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "genistein"
|
name = "genistein"
|
||||||
morning = 1
|
morning = 125
|
||||||
units = "caps"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "ginger root"
|
name = "ginger root"
|
||||||
morning = 2.2
|
morning = 2200
|
||||||
dinner = 2.2
|
dinner = 2200
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "glucosamine sulfate"
|
name = "glucosamine sulfate"
|
||||||
morning = 1500
|
morning = 1500
|
||||||
dinner = 1500
|
dinner = 1500
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "glycine"
|
name = "glycine"
|
||||||
morning = 2
|
morning = 2000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "hyaluronic acid"
|
name = "hyaluronic acid"
|
||||||
morning = 300
|
morning = 300
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "iodine"
|
name = "iodine"
|
||||||
morning = 125
|
morning = 125
|
||||||
units = "mcg"
|
units = "mcg"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "iron"
|
name = "iron"
|
||||||
morning = 10
|
morning = 10
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "k1"
|
name = "k1"
|
||||||
morning = 1.5
|
morning = 1500
|
||||||
|
units="mcg"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "k2-mk7"
|
name = "k2-mk7"
|
||||||
morning = 600
|
morning = 600
|
||||||
units = "mcg"
|
units = "mcg"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "k2-mk4"
|
name = "k2-mk4"
|
||||||
morning = 5
|
morning = 5
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "l-lysine"
|
name = "l-lysine"
|
||||||
morning = 1
|
morning = 1000
|
||||||
dinner = 1
|
dinner = 1000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "l-tyrosine"
|
name = "l-tyrosine"
|
||||||
dinner = 500
|
dinner = 500
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "lithium orotate"
|
name = "lithium orotate"
|
||||||
morning = 1
|
morning = 1
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "lycopene"
|
name = "lycopene"
|
||||||
morning = 10
|
morning = 10
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "magnesium slow release"
|
name = "magnesium slow release"
|
||||||
morning = 1
|
morning = 1
|
||||||
units = "caps"
|
units = "caps"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "magnesium taurate"
|
name = "magnesium taurate"
|
||||||
bedtime = 125
|
bedtime = 125
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "magnesium glycinate"
|
name = "magnesium glycinate"
|
||||||
bedtime = 350
|
bedtime = 350
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "magtein"
|
name = "magtein"
|
||||||
bedtime = 2
|
bedtime = 2000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "methyl folate"
|
name = "methyl folate"
|
||||||
morning = 1
|
morning = 1700
|
||||||
units = "caps"
|
units = "mcg"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "NAC"
|
name = "NAC"
|
||||||
morning = 1800
|
morning = 1800
|
||||||
dinner = 1800
|
dinner = 1800
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "pqq"
|
name = "pqq"
|
||||||
morning = 1
|
morning = 20
|
||||||
units = "caps"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "probiotic"
|
name = "probiotic"
|
||||||
morning = 1
|
morning = 1
|
||||||
units = "caps"
|
units = "caps"
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "taurine"
|
name = "taurine"
|
||||||
morning = 2
|
morning = 2000
|
||||||
dinner = 1
|
dinner = 1000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "turmeric"
|
name = "turmeric"
|
||||||
morning = 1
|
morning = 1000
|
||||||
dinner = 1
|
dinner = 1000
|
||||||
units = "g"
|
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "zeaxanthin"
|
name = "zeaxanthin"
|
||||||
morning = 20
|
morning = 20
|
||||||
per_week = 3
|
days_per_week = 3
|
||||||
|
|
||||||
[[ supps ]]
|
[[supps]]
|
||||||
name = "zinc"
|
name = "zinc"
|
||||||
morning = 15
|
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"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user