jkjk not final
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
**/*.csv
|
**/*.csv
|
||||||
.ipynb_checkpoints/
|
.ipynb_checkpoints/
|
||||||
|
__pycache__/
|
||||||
|
|||||||
77
analyze.py
77
analyze.py
@@ -7,80 +7,7 @@ import string
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def parse_foods_file():
|
from common import 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()
|
foods, recipes = parse_foods_file()
|
||||||
|
|
||||||
@@ -144,6 +71,8 @@ for fpath in sorted((Path.home() / 'workspace' / 'journal').glob('*.md')):
|
|||||||
daily_fat = 0.0
|
daily_fat = 0.0
|
||||||
daily_sugar = 0.0
|
daily_sugar = 0.0
|
||||||
|
|
||||||
|
output.write(f'-- {day}\n')
|
||||||
|
|
||||||
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
|
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())
|
||||||
|
|||||||
82
common.py
Normal file
82
common.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def parse_timestamp(timestamp):
|
||||||
|
return datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
24
extract_toc.py
Normal file
24
extract_toc.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from subprocess import run
|
||||||
|
import sys
|
||||||
|
|
||||||
|
outline = run(
|
||||||
|
['mutool', 'show', sys.argv[1], 'outline'],
|
||||||
|
capture_output=True
|
||||||
|
).stdout.decode('utf-8')
|
||||||
|
|
||||||
|
indent = 0
|
||||||
|
last_quote_index = 0
|
||||||
|
for line in outline.splitlines():
|
||||||
|
quote_index = line.find('"')
|
||||||
|
hash_index = line.find('#')
|
||||||
|
|
||||||
|
if quote_index > last_quote_index:
|
||||||
|
indent += 1
|
||||||
|
elif quote_index < last_quote_index:
|
||||||
|
indent -= 1
|
||||||
|
last_quote_index = quote_index
|
||||||
|
|
||||||
|
title = line[quote_index+1:line.find('"', quote_index+1)].strip()
|
||||||
|
page = int(line[hash_index+1:line.find(',', hash_index+1)])
|
||||||
|
|
||||||
|
print(f'{"#"*indent} {title} ({page})')
|
||||||
70
foods
70
foods
@@ -1,4 +1,34 @@
|
|||||||
|
|
||||||
|
ProteinRakha
|
||||||
|
Energy 67kcal
|
||||||
|
Fat 0.2g
|
||||||
|
SaturatedFat 0.1g
|
||||||
|
Carbs 6g
|
||||||
|
Sugar 5.9g
|
||||||
|
Protein 10g
|
||||||
|
Salt 0.06g
|
||||||
|
|
||||||
|
Water
|
||||||
|
Energy 0kcal
|
||||||
|
|
||||||
|
KaneliKorppu
|
||||||
|
Energy 359kcal
|
||||||
|
Fat 6.6g
|
||||||
|
SaturatedFat 0.5g
|
||||||
|
Carbs 65g
|
||||||
|
Sugar 23g
|
||||||
|
Fiber 4g
|
||||||
|
Protein 8g
|
||||||
|
Salt 0.5g
|
||||||
|
|
||||||
|
Broccoli
|
||||||
|
Energy 34kcal
|
||||||
|
Fat 0.4g
|
||||||
|
Carbs 7g
|
||||||
|
Fiber 2.6g
|
||||||
|
Sugar 1.7g
|
||||||
|
Protein 2.8g
|
||||||
|
|
||||||
Apple
|
Apple
|
||||||
Energy 52kcal
|
Energy 52kcal
|
||||||
Fat 0.2g
|
Fat 0.2g
|
||||||
@@ -7,6 +37,16 @@ Fiber 2.4g
|
|||||||
Sugar 10g
|
Sugar 10g
|
||||||
Protein 0.3g
|
Protein 0.3g
|
||||||
|
|
||||||
|
BeanitHarkis
|
||||||
|
Energy 206kcal
|
||||||
|
Fat 10g
|
||||||
|
SaturatedFat 1g
|
||||||
|
Carbs 9g
|
||||||
|
Sugar 0.9g
|
||||||
|
Fiber 6.1g
|
||||||
|
Protein 16g
|
||||||
|
Salt 1.3g
|
||||||
|
|
||||||
HarkisRouheseos
|
HarkisRouheseos
|
||||||
Energy 335kcal
|
Energy 335kcal
|
||||||
Fat 3.1g
|
Fat 3.1g
|
||||||
@@ -246,6 +286,16 @@ Sugar 22.8g
|
|||||||
Protein 1.2g
|
Protein 1.2g
|
||||||
Salt 1.8g
|
Salt 1.8g
|
||||||
|
|
||||||
|
WholeGrainBread
|
||||||
|
Energy 255kcal
|
||||||
|
Fat 5g
|
||||||
|
SaturatedFat 0.5g
|
||||||
|
Carbs 41g
|
||||||
|
Sugar 4g
|
||||||
|
Fiber 7g
|
||||||
|
Protein 8g
|
||||||
|
Salt 1g
|
||||||
|
|
||||||
WhiteBread
|
WhiteBread
|
||||||
Energy 253kcal
|
Energy 253kcal
|
||||||
Fat 2.5g
|
Fat 2.5g
|
||||||
@@ -740,3 +790,23 @@ HarkisRouheseos 180g
|
|||||||
Milk 240g
|
Milk 240g
|
||||||
Egg 65g
|
Egg 65g
|
||||||
GrahamFlour 60g
|
GrahamFlour 60g
|
||||||
|
|
||||||
|
RoastedVeggies2
|
||||||
|
Broccoli 300g
|
||||||
|
Oil 70g
|
||||||
|
Potato 1000g
|
||||||
|
SweetPotato 300g
|
||||||
|
|
||||||
|
BeanitHarkisVeggies
|
||||||
|
Oil 60g
|
||||||
|
BeanitHarkis 250g
|
||||||
|
PeasCornPepper 200g
|
||||||
|
Ketchup 100g
|
||||||
|
TOTAL 760g
|
||||||
|
|
||||||
|
Pancake
|
||||||
|
SkimMilk 430g
|
||||||
|
Water 300g
|
||||||
|
Oil 30g
|
||||||
|
Flour 430g
|
||||||
|
Sugar 20g
|
||||||
|
|||||||
39
parse.py
39
parse.py
@@ -43,7 +43,7 @@ header_modules = {
|
|||||||
'habits': (None, parse_habits),
|
'habits': (None, parse_habits),
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse_module(content: str) -> tuple[str, Union[list, dict]]:
|
def parse_header_module(content: str) -> tuple[str, Union[list, dict]]:
|
||||||
name, *content = content.splitlines()
|
name, *content = content.splitlines()
|
||||||
name = name.removesuffix(':').lower()
|
name = name.removesuffix(':').lower()
|
||||||
content = '\n'.join(content)
|
content = '\n'.join(content)
|
||||||
@@ -55,16 +55,25 @@ def parse_header(header):
|
|||||||
title = title.removeprefix('# ')
|
title = title.removeprefix('# ')
|
||||||
return {
|
return {
|
||||||
'title': title,
|
'title': title,
|
||||||
'modules': dict(parse_module(module) for module in modules)
|
'modules': dict(parse_header_module(module) for module in modules)
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_diet(content):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def parse_content(content):
|
||||||
|
content = content.strip()
|
||||||
|
return {
|
||||||
|
'blocks': [b.replace('\n', ' ') for b in content.split('\n\n')]
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse_timestamp(timestamp):
|
def parse_timestamp(timestamp):
|
||||||
return datetime.strptime('%Y-%m-%d %H:%M:%S')
|
return datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
def parse_entry(entry):
|
def parse_entry(entry):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
'timestamp': int(parse_timestamp(entry[:19]).timestamp()),
|
||||||
|
'content': parse_content(entry[19:]),
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse_file(fpath):
|
def parse_file(fpath):
|
||||||
@@ -73,33 +82,29 @@ def parse_file(fpath):
|
|||||||
|
|
||||||
buf = []
|
buf = []
|
||||||
|
|
||||||
with open(fpath) as fp:
|
for i, line in enumerate(fpath.read_text().splitlines()):
|
||||||
for i, line in enumerate(fp):
|
|
||||||
if entry_re.match(line):
|
if entry_re.match(line):
|
||||||
if not header:
|
if not header:
|
||||||
header = parse_header('\n'.join([c.strip() for c in buf]))
|
header = parse_header('\n'.join([c.strip() for c in buf]))
|
||||||
header['line'] = i
|
|
||||||
else:
|
else:
|
||||||
entries.append(parse_entry('\n'.join(buf)))
|
entries.append(parse_entry('\n'.join(buf)))
|
||||||
buf = [line]
|
buf = [line]
|
||||||
else:
|
else:
|
||||||
buf.append(line)
|
buf.append(line)
|
||||||
|
|
||||||
result = {
|
return {
|
||||||
'header': header,
|
'header': header,
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
breakpoint()
|
def parse_journal():
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for fpath in get_daily_file_paths()[-5:]:
|
||||||
|
info = parse_file(fpath)
|
||||||
|
result[info['header']['title']] = info
|
||||||
|
|
||||||
return result
|
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
|
import json
|
||||||
open('journal.json', 'w').write(json.dumps(parse_journal()))
|
open('journal.json', 'w').write(json.dumps(parse_journal()))
|
||||||
|
|||||||
99
progress.py
Normal file
99
progress.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import math
|
||||||
|
|
||||||
|
from common import parse_timestamp
|
||||||
|
|
||||||
|
content = open(sys.argv[1]).read().strip()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
* Total hours
|
||||||
|
* Hours today
|
||||||
|
* Hours this week
|
||||||
|
|
||||||
|
* Percentage completed
|
||||||
|
* Pages completed
|
||||||
|
|
||||||
|
* Estimated hours to completion
|
||||||
|
* Estimated completion date
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
lines = content.splitlines()
|
||||||
|
i = 0
|
||||||
|
current_chapter = ''
|
||||||
|
result = defaultdict(float)
|
||||||
|
|
||||||
|
total_chapters = 0
|
||||||
|
completed_chapters = 0
|
||||||
|
|
||||||
|
today = datetime.now().replace(hour=0,minute=0,second=0,microsecond=0)
|
||||||
|
this_week = today - timedelta(days=7)
|
||||||
|
|
||||||
|
total_hours = 0.0
|
||||||
|
day_hours = 0.0
|
||||||
|
week_hours = 0.0
|
||||||
|
|
||||||
|
oldest_timestamp = datetime.now()
|
||||||
|
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i].strip()
|
||||||
|
|
||||||
|
if line.startswith('#'):
|
||||||
|
current_chapter = line[line.find(' ')+1:]
|
||||||
|
total_chapters += 1
|
||||||
|
elif line.startswith('@start'):
|
||||||
|
start = parse_timestamp(line.removeprefix('@start '))
|
||||||
|
|
||||||
|
if start < oldest_timestamp:
|
||||||
|
oldest_timestamp = start
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
line = lines[i].strip()
|
||||||
|
|
||||||
|
end = parse_timestamp(line.removeprefix('@stop '))
|
||||||
|
|
||||||
|
delta = end - start
|
||||||
|
|
||||||
|
hours = delta.seconds / 60 / 60
|
||||||
|
|
||||||
|
result[current_chapter] += hours
|
||||||
|
total_hours += hours
|
||||||
|
|
||||||
|
if start > this_week:
|
||||||
|
week_hours += hours
|
||||||
|
if start > today:
|
||||||
|
day_hours += hours
|
||||||
|
|
||||||
|
elif line.startswith('@done'):
|
||||||
|
completed_chapters += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
#from pprint import pprint
|
||||||
|
#pprint(dict(result), sort_dicts=False)
|
||||||
|
|
||||||
|
num_days = (datetime.now() - oldest_timestamp).days or 1
|
||||||
|
hours_per_day = total_hours / num_days
|
||||||
|
|
||||||
|
hours_per_chapter = total_hours / completed_chapters
|
||||||
|
hours_to_completion = hours_per_chapter * (total_chapters - completed_chapters)
|
||||||
|
days_to_completion = math.ceil(hours_to_completion / hours_per_day)
|
||||||
|
|
||||||
|
completion_date = datetime.now() + timedelta(days=days_to_completion)
|
||||||
|
|
||||||
|
completion_percentage = completed_chapters/total_chapters*100
|
||||||
|
|
||||||
|
print(f'Started on: {oldest_timestamp.strftime("%Y-%m-%d")}')
|
||||||
|
print(f'Progress: [{completed_chapters}/{total_chapters}] ({round(completion_percentage, 2)}%)')
|
||||||
|
print(f'Total: {round(total_hours, 2)}h')
|
||||||
|
print(f'Week: {round(week_hours, 2)}h')
|
||||||
|
print(f'Day: {round(day_hours, 2)}h')
|
||||||
|
print(f'Hours per day: {round(hours_per_day, 2)}h')
|
||||||
|
print(f'Hours to completion: {round(hours_to_completion, 2)}h')
|
||||||
|
print(f'Completion date: {completion_date.strftime("%Y-%m-%d")}')
|
||||||
|
|
||||||
Reference in New Issue
Block a user