diff --git a/main.py b/main.py new file mode 100644 index 0000000..df0f3ef --- /dev/null +++ b/main.py @@ -0,0 +1,85 @@ +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) diff --git a/scrape.js b/scrape.js index d66dd9e..6804f01 100644 --- a/scrape.js +++ b/scrape.js @@ -1,7 +1,6 @@ const getOrderElements = () => Array.from(document.querySelectorAll(".order-details-box")) function getDateOfOrder(orderEl) { - console.log(orderEl); return Date.parse( orderEl .children[0] @@ -31,6 +30,38 @@ SERVINGS_LOOKUP = { numUnitsInServing: .126, servingUnit: "ml", }, + "Magnesium Taurate": { + numUnitsInServing: 1, + servingUnit: "cap", + }, + "High Absorption Magnesium Glycinate 350": { + numUnitsInServing: 350, + servingUnit: "mg", + }, + "Iron Bisglycinate": { + numUnitsInServing: 1, + servingUnit: "cap", + }, + "Extend-Release Magnesium": { + numUnitsInServing: 1, + servingUnit: "cap", + }, + "Lutein & Zeaxanthin": { + numUnitsInServing: 1, + servingUnit: "cap", + }, + "Aged Garlic Extract": { + numUnitsInServing: 600, + servingUnit: "mg", + }, + "Organic Turmeric Curcumin": { + numUnitsInServing: 2250, + servingUnit: "mg", + }, + "Lithium Orotate Drops": { + numUnitsInServing: .25, + servingUnit: "ml", + }, }; const getNumBottles = orderEl => parseInt(orderEl @@ -55,9 +86,16 @@ function makeSuppObj(sup) { const ord = sup.parentElement.parentElement.parentElement; const parts = sup.children[1].children[0].innerText.split(", "); - // TODO: if '(' in quantityText, use that as the serving - // in order to get the number of servings on hand - const quantityText = parts.slice(-1)[0]; + let quantityText = parts.slice(-1)[0]; + let origQtyTxt = quantityText; + if (quantityText.includes("(")) { + quantityText = quantityText.match(/\((\d+)/).slice(-1)[0] + } + let quantityUnits = "caps"; + if (!["caps", "capsules", "tablets", "softgels", "vegcaps", "lozenges"].some(str => origQtyTxt.toLowerCase().includes(str))) { + quantityUnits = origQtyTxt.match(/\(\d+ (.*)\)/).slice(-1)[0]; + console.log(quantityUnits); + } const servingText = parts.slice(-2, -1)[0]; const name = parts[1]; if (SKIP_THESE.includes(name)) { @@ -76,7 +114,6 @@ function makeSuppObj(sup) { toString: function(){return this.name + ": " + this.message;} }; } else { - console.log(servingObj); ({ numUnitsInServing, servingUnit } = servingObj); } } else { @@ -86,14 +123,28 @@ function makeSuppObj(sup) { if (typeof numUnitsInServing !== "number") { numUnitsInServing = numUnitsInServing.replace(",", ""); } - numUnitsInServing = parseInt(numUnitsInServing); + let numUnitsInServingFinal = parseInt(numUnitsInServing); + if (!numUnitsInServingFinal || numUnitsInServingFinal == 0 || name.includes(servingUnit)) { + const servingObj = SERVINGS_LOOKUP[name]; + if (!servingObj) { + throw { + name: "servingError", + level: "serious", + message: `couldn't get serving info for ${name} '(${parts})'`, + htmlMessage: this.message, + toString: function(){return this.name + ": " + this.message;} + }; + } + ({ numUnitsInServing: numUnitsInServingFinal, servingUnit } = servingObj); + } return { orderDate: new Date(getDateOfOrder(ord)), name, quantity: parseInt(quantityText.split(" ")[0]), + quantityUnits, servingUnit, - numUnitsInServing, + numUnitsInServing: numUnitsInServingFinal, numBottles: getNumBottles(ord), } } @@ -101,10 +152,15 @@ function makeSuppObj(sup) { function main(orderCutoffHuman) { const orderCutoff = Date.parse(orderCutoffHuman); - const orderEls = getOrderElements().filter(el => getDateOfOrder(el) > orderCutoff) + const orderEls = getOrderElements() + .filter(el => !el.innerText.includes("Cancelled")) + .filter(el => getDateOfOrder(el) > orderCutoff) let suppEls = []; for (const orderEl of orderEls) { suppEls = suppEls.concat(getSupps(orderEl)); } + // TODO: write this to a file or copy to clipboard in CSV return suppEls.map(suppEl => makeSuppObj(suppEl)).filter(res => res != null); } + +console.log(JSON.stringify(main("January 04, 2024"))); diff --git a/supps.json b/supps.json new file mode 100644 index 0000000..3d3c554 --- /dev/null +++ b/supps.json @@ -0,0 +1,443 @@ +[ + { + "orderDate": "2023-10-14:00:00.000Z", + "name": "Ultimate Omega, Lemon", + "quantity": 180, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 640, + "numBottles": 1 + }, + { + "orderDate": "2023-10-14:00:00.000Z", + "name": "Super K", + "quantity": 90, + "quantityUnits": "caps", + "servingUnit": "caps", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2022-09-19:00:00.000Z", + "name": "High Absorption CoQ10 with BioPerine", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 120, + "numBottles": 2 + }, + { + "orderDate": "2024-01-30T23:00:00.000Z", + "name": "NAC N-Acetyl Cysteine", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 1000, + "numBottles": 4 + }, + { + "orderDate": "2024-01-02:00:00.000Z", + "name": "Glucosamine Sulfate", + "quantity": 240, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 750, + "numBottles": 1 + }, + { + "orderDate": "2024-01-02:00:00.000Z", + "name": "Vitamin K2, MK-4 (Menatetrenone)", + "quantity": 180, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 5, + "numBottles": 1 + }, + { + "orderDate": "2023-07-03:00:00.000Z", + "name": "Glycine", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "caps", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Fisetin Novusetin", + "quantity": 30, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 100, + "numBottles": 4 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "MK-7 Vitamin K-2", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mcg", + "numUnitsInServing": 300, + "numBottles": 4 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Liquid D-3 & MK-7", + "quantity": 30, + "quantityUnits": "ml", + "servingUnit": "ml", + "numUnitsInServing": 0.368, + "numBottles": 4 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Calcium AKG Longevity", + "quantity": 500, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 1000, + "numBottles": 4 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Magtein", + "quantity": 180, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 667, + "numBottles": 1 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Magnesium Taurate", + "quantity": 180, + "quantityUnits": "caps", + "servingUnit": "cap", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Collagen", + "quantity": 290, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 6000, + "numBottles": 1 + }, + { + "orderDate": "2024-01-23T23:00:00.000Z", + "name": "Ginger Root", + "quantity": 100, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 550, + "numBottles": 1 + }, + { + "orderDate": "2024-01-09T23:00:00.000Z", + "name": "PQQ", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 20, + "numBottles": 1 + }, + { + "orderDate": "2023-08-02T23:00:00.000Z", + "name": "B-50", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "caps", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2024-01-09T23:00:00.000Z", + "name": "Magnesium Taurate +", + "quantity": 200, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 400, + "numBottles": 1 + }, + { + "orderDate": "2024-01-09T23:00:00.000Z", + "name": "High Absorption Magnesium Glycinate 350", + "quantity": 160, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 350, + "numBottles": 1 + }, + { + "orderDate": "2024-01-05T23:00:00.000Z", + "name": "Ginger Root", + "quantity": 100, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 550, + "numBottles": 1 + }, + { + "orderDate": "2024-01-05T23:00:00.000Z", + "name": "Probiotic-10", + "quantity": 50, + "quantityUnits": "caps", + "servingUnit": "Billion", + "numUnitsInServing": 25, + "numBottles": 1 + }, + { + "orderDate": "2024-01-05T23:00:00.000Z", + "name": "Iron Bisglycinate", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "cap", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2024-01-05T23:00:00.000Z", + "name": "Optimized Folate", + "quantity": 100, + "quantityUnits": "caps", + "servingUnit": "mcg", + "numUnitsInServing": 1700, + "numBottles": 1 + }, + { + "orderDate": "2024-01-05T23:00:00.000Z", + "name": "Extend-Release Magnesium", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "cap", + "numUnitsInServing": 1, + "numBottles": 1 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Fisetin with Novusetin", + "quantity": 30, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 100, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Genistein from Sophora Japonica", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 125, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Aged Garlic Extract", + "quantity": 300, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 600, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Crucera-SGS", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "cap", + "numUnitsInServing": 1, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Double Strength Taurine", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 1000, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Ashwagandha", + "quantity": 120, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 300, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Astaxanthin", + "quantity": 120, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 12, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Methyl B-12", + "quantity": 100, + "quantityUnits": "caps", + "servingUnit": "mcg", + "numUnitsInServing": 1000, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Boron", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 3, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Calcium AKG Longevity", + "quantity": 500, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 1000, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Organic Turmeric Curcumin", + "quantity": 180, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 2250, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Lutein & Zeaxanthin", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "cap", + "numUnitsInServing": 1, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Zinc Citrate", + "quantity": 90, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 15, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "NAD+ Cell Regenerator", + "quantity": 30, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 300, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Creatine", + "quantity": 450, + "quantityUnits": "g", + "servingUnit": "g", + "numUnitsInServing": 5, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Lycopene", + "quantity": 120, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 10, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Vitamin C", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 500, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Natural Vitamin K2 MK-7 with MenaQ7", + "quantity": 60, + "quantityUnits": "caps", + "servingUnit": "mcg", + "numUnitsInServing": 100, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "L-Lysine", + "quantity": 250, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 1000, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "L-Tyrosine", + "quantity": 120, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 500, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Lithium Orotate Drops", + "quantity": 59, + "quantityUnits": "ml", + "servingUnit": "ml", + "numUnitsInServing": 0.25, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Liquid Iodine Plus", + "quantity": 59, + "quantityUnits": "ml", + "servingUnit": "ml", + "numUnitsInServing": 0.126, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Hyaluronic Acid", + "quantity": 120, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 100, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Aspirin", + "quantity": 300, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 81, + "numBottles": 2 + }, + { + "orderDate": "2024-01-04T23:00:00.000Z", + "name": "Naturally Sourced Vitamin E", + "quantity": 100, + "quantityUnits": "caps", + "servingUnit": "mg", + "numUnitsInServing": 134, + "numBottles": 2 + } +] diff --git a/supps.toml b/supps.toml new file mode 100644 index 0000000..15b09a5 --- /dev/null +++ b/supps.toml @@ -0,0 +1,207 @@ +[[ supps ]] +name = "ashwagandha" +bedtime = 600 + +[[ supps ]] +name = "aspirin" +lunch = 86 +per_week = 3 + +[[ supps ]] +name = "astaxanthin" +morning = 12 + +[[ supps ]] +name = "b complex" +morning = 0.5 +per_week = 2 +units = "caps" + +[[ supps ]] +name = "b-12" +lunch = 1000 +units = "mcg" +per_week = 1 + +[[ supps ]] +name = "boron" +morning = 2 + +[[ supps ]] +name = "broccomax" +morning = 17.5 +dinner = 17.5 + +[[ supps ]] +name = "vitamin c" +morning = 500 + +[[ supps ]] +name = "creatine" +lunch = 2.5 +units = "g" +per_week = 4 + +[[ supps ]] +name = "Ca-AKG" +morning = 1 +dinner = 1 +units = "g" + +[[ supps ]] +name = "coq10" +morning = 100 +lunch = 100 +dinner = 100 + +[[ supps ]] +name = "d-3" +morning = 5000 +units = "iu" +winter_only = true + +[[ supps ]] +name = "DHEA" +morning = 25 + +[[ supps ]] +name = "vitamin e" +morning = 200 +per_week = 3 +units = "iu" + +[[ supps ]] +name = "EPA/DHA" +morning = 2 +dinner = 1 +units = "caps" + +[[ supps ]] +name = "garlic" +morning = 1.2 +dinner = 1.2 +units = "g" + +[[ supps ]] +name = "genistein" +morning = 1 +units = "caps" + +[[ supps ]] +name = "ginger root" +morning = 2.2 +dinner = 2.2 +units = "g" + +[[ supps ]] +name = "glucosamine sulfate" +morning = 1500 +dinner = 1500 + +[[ supps ]] +name = "glycine" +morning = 2 +units = "g" + +[[ supps ]] +name = "hyaluronic acid" +morning = 300 + +[[ supps ]] +name = "iodine" +morning = 125 +units = "mcg" + +[[ supps ]] +name = "iron" +morning = 10 + +[[ supps ]] +name = "k1" +morning = 1.5 + +[[ supps ]] +name = "k2-mk7" +morning = 600 +units = "mcg" + +[[ supps ]] +name = "k2-mk4" +morning = 5 + +[[ supps ]] +name = "l-lysine" +morning = 1 +dinner = 1 +units = "g" + +[[ supps ]] +name = "l-tyrosine" +dinner = 500 + +[[ supps ]] +name = "lithium orotate" +morning = 1 + +[[ supps ]] +name = "lycopene" +morning = 10 + +[[ supps ]] +name = "magnesium slow release" +morning = 1 +units = "caps" + +[[ supps ]] +name = "magnesium taurate" +bedtime = 125 + +[[ supps ]] +name = "magnesium glycinate" +bedtime = 350 + +[[ supps ]] +name = "magtein" +bedtime = 2 +units = "g" + +[[ supps ]] +name = "methyl folate" +morning = 1 +units = "caps" + +[[ supps ]] +name = "NAC" +morning = 1800 +dinner = 1800 + +[[ supps ]] +name = "pqq" +morning = 1 +units = "caps" + +[[ supps ]] +name = "probiotic" +morning = 1 +units = "caps" + +[[ supps ]] +name = "taurine" +morning = 2 +dinner = 1 +units = "g" + +[[ supps ]] +name = "turmeric" +morning = 1 +dinner = 1 +units = "g" + +[[ supps ]] +name = "zeaxanthin" +morning = 20 +per_week = 3 + +[[ supps ]] +name = "zinc" +morning = 15