From 3aef7c413361e75638719b50bcc0996def8462b6 Mon Sep 17 00:00:00 2001 From: olari Date: Fri, 28 May 2021 04:13:27 +0300 Subject: [PATCH] jkjk not final --- .gitignore | 1 + analyze.py | 77 ++------------------------------------- common.py | 82 +++++++++++++++++++++++++++++++++++++++++ extract_toc.py | 24 ++++++++++++ foods | 70 +++++++++++++++++++++++++++++++++++ parse.py | 53 +++++++++++++++------------ progress.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 308 insertions(+), 98 deletions(-) create mode 100644 common.py create mode 100644 extract_toc.py create mode 100644 progress.py diff --git a/.gitignore b/.gitignore index f3a95a0..8562add 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/*.csv .ipynb_checkpoints/ +__pycache__/ diff --git a/analyze.py b/analyze.py index 71a4465..dca9fc5 100644 --- a/analyze.py +++ b/analyze.py @@ -7,80 +7,7 @@ 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 +from common import 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_sugar = 0.0 + output.write(f'-- {day}\n') + 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()) diff --git a/common.py b/common.py new file mode 100644 index 0000000..bbb51cf --- /dev/null +++ b/common.py @@ -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 + + diff --git a/extract_toc.py b/extract_toc.py new file mode 100644 index 0000000..d808e1f --- /dev/null +++ b/extract_toc.py @@ -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})') diff --git a/foods b/foods index 5b891da..aa85c97 100644 --- a/foods +++ b/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 Energy 52kcal Fat 0.2g @@ -7,6 +37,16 @@ Fiber 2.4g Sugar 10g Protein 0.3g +BeanitHarkis +Energy 206kcal +Fat 10g +SaturatedFat 1g +Carbs 9g +Sugar 0.9g +Fiber 6.1g +Protein 16g +Salt 1.3g + HarkisRouheseos Energy 335kcal Fat 3.1g @@ -246,6 +286,16 @@ Sugar 22.8g Protein 1.2g Salt 1.8g +WholeGrainBread +Energy 255kcal +Fat 5g +SaturatedFat 0.5g +Carbs 41g +Sugar 4g +Fiber 7g +Protein 8g +Salt 1g + WhiteBread Energy 253kcal Fat 2.5g @@ -740,3 +790,23 @@ HarkisRouheseos 180g Milk 240g Egg 65g 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 diff --git a/parse.py b/parse.py index e779b37..20a8a43 100644 --- a/parse.py +++ b/parse.py @@ -43,7 +43,7 @@ header_modules = { '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 = name.removesuffix(':').lower() content = '\n'.join(content) @@ -55,16 +55,25 @@ def parse_header(header): title = title.removeprefix('# ') return { '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): - return datetime.strptime('%Y-%m-%d %H:%M:%S') + return datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S') def parse_entry(entry): - return { - + 'timestamp': int(parse_timestamp(entry[:19]).timestamp()), + 'content': parse_content(entry[19:]), } def parse_file(fpath): @@ -73,33 +82,29 @@ def parse_file(fpath): 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] + for i, line in enumerate(fpath.read_text().splitlines()): + if entry_re.match(line): + if not header: + header = parse_header('\n'.join([c.strip() for c in buf])) else: - buf.append(line) + entries.append(parse_entry('\n'.join(buf))) + buf = [line] + else: + buf.append(line) - result = { + return { 'header': header, '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 -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())) diff --git a/progress.py b/progress.py new file mode 100644 index 0000000..9465858 --- /dev/null +++ b/progress.py @@ -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")}') +