final commit
This commit is contained in:
@@ -129,6 +129,8 @@ diet_csv = [[
|
|||||||
'saturated_fat', 'sugar', 'fiber'
|
'saturated_fat', 'sugar', 'fiber'
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
output = open('diet', 'w')
|
||||||
|
|
||||||
for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
||||||
day = fpath.stem
|
day = fpath.stem
|
||||||
header, *tmp = entry_re.split(fpath.read_text())
|
header, *tmp = entry_re.split(fpath.read_text())
|
||||||
@@ -143,6 +145,7 @@ for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
|||||||
daily_sugar = 0.0
|
daily_sugar = 0.0
|
||||||
|
|
||||||
for (timestamp, content) in sorted(entries, key=lambda x: x[0]):
|
for (timestamp, content) in sorted(entries, key=lambda x: x[0]):
|
||||||
|
ts_str = timestamp
|
||||||
timestamp = int(datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S').timestamp())
|
timestamp = int(datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S').timestamp())
|
||||||
|
|
||||||
content = '\n'.join(
|
content = '\n'.join(
|
||||||
@@ -152,8 +155,11 @@ for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
|||||||
|
|
||||||
for diet in diet_re.finditer(content):
|
for diet in diet_re.finditer(content):
|
||||||
value, name = diet.groups()
|
value, name = diet.groups()
|
||||||
|
output.write(f'{ts_str} {name} {value}\n')
|
||||||
|
|
||||||
value = float(value.removesuffix('g'))
|
value = float(value.removesuffix('g'))
|
||||||
|
|
||||||
|
|
||||||
if name in recipes:
|
if name in recipes:
|
||||||
food = recipes[name]
|
food = recipes[name]
|
||||||
|
|
||||||
@@ -172,6 +178,7 @@ for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
diet_csv.append((
|
diet_csv.append((
|
||||||
timestamp,
|
timestamp,
|
||||||
name,
|
name,
|
||||||
|
|||||||
70
foods
70
foods
@@ -1,4 +1,32 @@
|
|||||||
|
|
||||||
|
Apple
|
||||||
|
Energy 52kcal
|
||||||
|
Fat 0.2g
|
||||||
|
Carbs 14g
|
||||||
|
Fiber 2.4g
|
||||||
|
Sugar 10g
|
||||||
|
Protein 0.3g
|
||||||
|
|
||||||
|
HarkisRouheseos
|
||||||
|
Energy 335kcal
|
||||||
|
Fat 3.1g
|
||||||
|
SaturatedFat 0.3g
|
||||||
|
Carbs 26g
|
||||||
|
Sugar 3.7g
|
||||||
|
Fiber 12g
|
||||||
|
Protein 47g
|
||||||
|
Salt 1.0g
|
||||||
|
|
||||||
|
HazelnutIcecream
|
||||||
|
Energy 288kcal
|
||||||
|
Fat 18g
|
||||||
|
SaturatedFat 5.6g
|
||||||
|
Carbs 27g
|
||||||
|
Sugar 24g
|
||||||
|
Fiber 1.1g
|
||||||
|
Protein 4.0g
|
||||||
|
Salt 0.17g
|
||||||
|
|
||||||
CheesePizza
|
CheesePizza
|
||||||
Energy 210kcal
|
Energy 210kcal
|
||||||
Fat 5.3g
|
Fat 5.3g
|
||||||
@@ -129,6 +157,15 @@ Fiber 2.4g
|
|||||||
Protein 11.4g
|
Protein 11.4g
|
||||||
Salt 0.01g
|
Salt 0.01g
|
||||||
|
|
||||||
|
GrahamFlour
|
||||||
|
Energy 330kcal
|
||||||
|
Fat 3.0g
|
||||||
|
SaturatedFat 0.5g
|
||||||
|
Carbs 56g
|
||||||
|
Sugar 0.8g
|
||||||
|
Fiber 13g
|
||||||
|
Protein 14g
|
||||||
|
|
||||||
Salt
|
Salt
|
||||||
Energy 0kcal
|
Energy 0kcal
|
||||||
Salt 100g
|
Salt 100g
|
||||||
@@ -554,6 +591,12 @@ Carbs 1.5g
|
|||||||
Protein 22.0g
|
Protein 22.0g
|
||||||
Salt 2.7g
|
Salt 2.7g
|
||||||
|
|
||||||
|
Salmon
|
||||||
|
Energy 208kcal
|
||||||
|
Fat 13g
|
||||||
|
SaturatedFat 3.0g
|
||||||
|
Protein 20g
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Omlette
|
Omlette
|
||||||
@@ -590,6 +633,10 @@ FakeChicken 250g
|
|||||||
Oil 40g
|
Oil 40g
|
||||||
PeasCornPepper 225g
|
PeasCornPepper 225g
|
||||||
|
|
||||||
|
OilVeggies
|
||||||
|
Oil 30g
|
||||||
|
PeasCornPepper 220g
|
||||||
|
|
||||||
MashedPotato
|
MashedPotato
|
||||||
Margarine 40g
|
Margarine 40g
|
||||||
Milk 180g
|
Milk 180g
|
||||||
@@ -633,6 +680,11 @@ FoodCream 450g
|
|||||||
Oil 50g
|
Oil 50g
|
||||||
Potato 1500g
|
Potato 1500g
|
||||||
|
|
||||||
|
CreamPotatoes2
|
||||||
|
Cheese 200g
|
||||||
|
FoodCream 450g
|
||||||
|
Potato 1020g
|
||||||
|
|
||||||
ProteinSpaghettiBolognese
|
ProteinSpaghettiBolognese
|
||||||
ProteinSpaghetti 500g
|
ProteinSpaghetti 500g
|
||||||
PastaSauce 500g
|
PastaSauce 500g
|
||||||
@@ -666,7 +718,25 @@ Oil 20g
|
|||||||
SaladDressing 65g
|
SaladDressing 65g
|
||||||
TOTAL 1675g
|
TOTAL 1675g
|
||||||
|
|
||||||
|
PastaSalad2
|
||||||
|
TricolorePasta 500g
|
||||||
|
Salad 270g
|
||||||
|
Oil 30g
|
||||||
|
SaladDressing 100g
|
||||||
|
TOTAL 1675g
|
||||||
|
|
||||||
ProteinShake
|
ProteinShake
|
||||||
SkimMilk 400g
|
SkimMilk 400g
|
||||||
ProteinPowder 60g
|
ProteinPowder 60g
|
||||||
|
|
||||||
|
MashedPotato2
|
||||||
|
Margarine 20g
|
||||||
|
Potato 760g
|
||||||
|
Milk 300g
|
||||||
|
|
||||||
|
VegeKotlet
|
||||||
|
Oil 45g
|
||||||
|
HarkisRouheseos 180g
|
||||||
|
Milk 240g
|
||||||
|
Egg 65g
|
||||||
|
GrahamFlour 60g
|
||||||
|
|||||||
105
parse.py
Normal file
105
parse.py
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
from subprocess import run
|
||||||
|
from typing import Union
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
JOURNAL_PATH = '/Users/olari/workspace/journal'
|
||||||
|
|
||||||
|
entry_re = re.compile(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}')
|
||||||
|
|
||||||
|
def get_daily_file_paths() -> list[Path]:
|
||||||
|
return list(sorted(Path(JOURNAL_PATH).glob('*.md')))
|
||||||
|
|
||||||
|
def resolve_relative_time_expression(now: datetime, expr: str) -> datetime:
|
||||||
|
weekdays = [
|
||||||
|
'monday', 'tuesday', 'wednesday', 'thursday',
|
||||||
|
'friday', 'saturday', 'sunday'
|
||||||
|
]
|
||||||
|
|
||||||
|
if expr == 'today':
|
||||||
|
return now
|
||||||
|
elif expr == 'tomorrow':
|
||||||
|
return now + timedelta(days=1)
|
||||||
|
elif expr == 'yesterday':
|
||||||
|
return now - timedelta(days=1)
|
||||||
|
elif expr in weekdays:
|
||||||
|
return now - timedelta(days=now.weekday()) + weekdays.index(expr)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def parse_godword(content: str) -> list[str]:
|
||||||
|
return content.split()
|
||||||
|
|
||||||
|
def parse_habits(content: str) -> dict[str, bool]:
|
||||||
|
return {
|
||||||
|
line[4:]: line[1] == 'x'
|
||||||
|
for line in content.splitlines()
|
||||||
|
}
|
||||||
|
|
||||||
|
header_modules = {
|
||||||
|
'godword': (None, parse_godword),
|
||||||
|
'habits': (None, parse_habits),
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_module(content: str) -> tuple[str, Union[list, dict]]:
|
||||||
|
name, *content = content.splitlines()
|
||||||
|
name = name.removesuffix(':').lower()
|
||||||
|
content = '\n'.join(content)
|
||||||
|
_, parse = header_modules[name]
|
||||||
|
return name, parse(content)
|
||||||
|
|
||||||
|
def parse_header(header):
|
||||||
|
title, *modules = header.split('\n\n')
|
||||||
|
title = title.removeprefix('# ')
|
||||||
|
return {
|
||||||
|
'title': title,
|
||||||
|
'modules': dict(parse_module(module) for module in modules)
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_timestamp(timestamp):
|
||||||
|
return datetime.strptime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
def parse_entry(entry):
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_file(fpath):
|
||||||
|
header = {}
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
buf = []
|
||||||
|
|
||||||
|
with open(fpath) as fp:
|
||||||
|
for i, line in enumerate(fp):
|
||||||
|
if entry_re.match(line):
|
||||||
|
if not header:
|
||||||
|
header = parse_header('\n'.join([c.strip() for c in buf]))
|
||||||
|
header['line'] = i
|
||||||
|
else:
|
||||||
|
entries.append(parse_entry('\n'.join(buf)))
|
||||||
|
buf = [line]
|
||||||
|
else:
|
||||||
|
buf.append(line)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'header': header,
|
||||||
|
'entries': entries,
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoint()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def parse_journal():
|
||||||
|
return {
|
||||||
|
info['header']['title']: info
|
||||||
|
for fpath in get_daily_file_paths()[-5:]
|
||||||
|
for info in parse_file(fpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
import json
|
||||||
|
open('journal.json', 'w').write(json.dumps(parse_journal()))
|
||||||
29
search.py
Normal file
29
search.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from subprocess import run
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
entry_re = re.compile(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', re.MULTILINE)
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
|
||||||
|
for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
||||||
|
header, *tmp = entry_re.split(fpath.read_text())
|
||||||
|
entries = list(zip(tmp[::2], tmp[1::2]))
|
||||||
|
|
||||||
|
for (timestamp, content) in sorted(entries, key=lambda x: x[0]):
|
||||||
|
content = '\n'.join(
|
||||||
|
part.replace('\n', ' ')
|
||||||
|
for part in content.split('\n\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
if sys.argv[1].lower() in content.lower().split():
|
||||||
|
matches.append((timestamp, content))
|
||||||
|
|
||||||
|
buf = ''
|
||||||
|
|
||||||
|
for (ts, c) in matches:
|
||||||
|
c = c.replace('\n', ' ').strip()
|
||||||
|
buf += (f'[[{ts}]] {c}')[:80] + '\n'
|
||||||
|
|
||||||
|
run(['nvim', '-'], input=buf.encode('utf-8'))
|
||||||
151
summary.py
Normal file
151
summary.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
from collections import Counter
|
||||||
|
from functools import reduce
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def parse_foods_file():
|
||||||
|
path = Path.home() / 'projects' / 'open-journal' / 'foods'
|
||||||
|
text = path.read_text()
|
||||||
|
foods, recipes = text.split('---')
|
||||||
|
|
||||||
|
def parse_macro(macro):
|
||||||
|
if macro == '...':
|
||||||
|
return ('INVALID', 0.0)
|
||||||
|
|
||||||
|
name, value = macro.split()
|
||||||
|
value = float(value.removesuffix('g').removesuffix('kcal'))
|
||||||
|
return (name, value)
|
||||||
|
|
||||||
|
foods = {
|
||||||
|
macros[0]: dict(parse_macro(macro) for macro in macros[1:])
|
||||||
|
for macros in [food.split('\n') for food in foods.strip().split('\n\n')]
|
||||||
|
}
|
||||||
|
|
||||||
|
def combine_values(fst, snd):
|
||||||
|
result = fst.copy()
|
||||||
|
for k,v in snd.items():
|
||||||
|
if k in fst:
|
||||||
|
result[k] += v
|
||||||
|
else:
|
||||||
|
result[k] = v
|
||||||
|
return result
|
||||||
|
|
||||||
|
def evaluate_ingredients(ingredients):
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
total_weight = 0.0
|
||||||
|
for ingredient in ingredients:
|
||||||
|
k,v = parse_macro(ingredient)
|
||||||
|
if k == 'TOTAL':
|
||||||
|
result[k] = v
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
total_weight += v
|
||||||
|
|
||||||
|
|
||||||
|
food = foods[k]
|
||||||
|
|
||||||
|
for kk,vv in food.items():
|
||||||
|
if kk not in result:
|
||||||
|
result[kk] = 0.0
|
||||||
|
|
||||||
|
result[kk] += vv * (v/100.0)
|
||||||
|
|
||||||
|
if 'TOTAL' not in result:
|
||||||
|
result['TOTAL'] = total_weight
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
recipes = {
|
||||||
|
ingredients[0]: evaluate_ingredients(ingredients[1:])
|
||||||
|
for ingredients in [
|
||||||
|
recipe.split('\n') for recipe in recipes.strip().split('\n\n')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_calories_from_macros(mm):
|
||||||
|
calories = 0.0
|
||||||
|
for k,v in mm.items():
|
||||||
|
calories += v * {
|
||||||
|
'Carbs': 4,
|
||||||
|
'Fat': 9,
|
||||||
|
'Protein': 4
|
||||||
|
}.get(k, 0.0)
|
||||||
|
return calories
|
||||||
|
|
||||||
|
#for k,v in foods.items():
|
||||||
|
# print(round(v.get('Energy') - get_calories_from_macros(v)), k)
|
||||||
|
|
||||||
|
return foods, recipes
|
||||||
|
|
||||||
|
foods, recipes = parse_foods_file()
|
||||||
|
|
||||||
|
entry_re = re.compile(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ', re.MULTILINE)
|
||||||
|
diet_re = re.compile(r'@diet (\d+g) ([a-zA-Z]+)')
|
||||||
|
|
||||||
|
current_day = list(sorted((Path.home() / 'workspace' /
|
||||||
|
'journal').glob('*.md')))[-1]
|
||||||
|
|
||||||
|
header, *tmp = entry_re.split(current_day.read_text())
|
||||||
|
entries = list(zip(tmp[::2], tmp[1::2]))
|
||||||
|
|
||||||
|
daily_grams = 0.0
|
||||||
|
daily_calories = 0.0
|
||||||
|
daily_protein = 0.0
|
||||||
|
|
||||||
|
for (timestamp, content) in sorted(entries, key=lambda x: x[0]):
|
||||||
|
content = '\n'.join(
|
||||||
|
part.replace('\n', ' ')
|
||||||
|
for part in content.split('\n\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
has_printed = False
|
||||||
|
entry_calories = 0.0
|
||||||
|
entry_protein = 0.0
|
||||||
|
for diet in diet_re.finditer(content):
|
||||||
|
if not has_printed:
|
||||||
|
print(f'-- {timestamp}')
|
||||||
|
has_printed = True
|
||||||
|
|
||||||
|
value, name = diet.groups()
|
||||||
|
value = float(value.removesuffix('g'))
|
||||||
|
|
||||||
|
if name in recipes:
|
||||||
|
food = recipes[name]
|
||||||
|
|
||||||
|
if value == 0.0:
|
||||||
|
value = food['TOTAL']
|
||||||
|
|
||||||
|
food = {k: v*(value/food['TOTAL']) for k,v in food.items()}
|
||||||
|
elif name in foods:
|
||||||
|
if value == 0.0:
|
||||||
|
value = 100
|
||||||
|
|
||||||
|
food = {k: v*(value/100.0) for k,v in foods[name].items()}
|
||||||
|
else:
|
||||||
|
breakpoint()
|
||||||
|
print(f'ERROR: Invalid diet entry: {content}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
protein = round(food.get('Protein', 0.0), 2)
|
||||||
|
calories = round(food.get('Energy', 0.0), 2)
|
||||||
|
|
||||||
|
entry_calories += calories
|
||||||
|
entry_protein += protein
|
||||||
|
|
||||||
|
print(f'{name:<20} {value:<6}g, {calories:<6}kcal, {protein:<6}g protein')
|
||||||
|
|
||||||
|
if has_printed:
|
||||||
|
entry_calories = round(entry_calories, 2)
|
||||||
|
entry_protein = round(entry_protein, 2)
|
||||||
|
print(f'-- TOTAL: {entry_calories}kcal, {entry_protein}g protein')
|
||||||
|
print()
|
||||||
|
|
||||||
|
daily_calories += entry_calories
|
||||||
|
daily_protein += entry_protein
|
||||||
|
|
||||||
|
print(f'-- DAILY TOTAL ({daily_calories}kcal, {daily_protein}g protein)')
|
||||||
Reference in New Issue
Block a user