From 760d072982f058e0c46d7afdc9d115aae4b1e520 Mon Sep 17 00:00:00 2001 From: olari Date: Sun, 27 Jun 2021 11:59:33 +0300 Subject: [PATCH] read-only qs section --- journal.py | 212 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 155 insertions(+), 57 deletions(-) diff --git a/journal.py b/journal.py index 3c5e873..8c6dacd 100644 --- a/journal.py +++ b/journal.py @@ -193,6 +193,17 @@ def parse_tasks_file(text): return result +### HACKY HACKERY + +_GLOBAL_DO_NOT_USE = {} + +def init_hacky_hackery(journal): + global _GLOBAL_DO_NOT_USE + _GLOBAL_DO_NOT_USE = journal + +def get_foods_file(): + return parse_foods_file(_GLOBAL_DO_NOT_USE['files']['foods']) + ### HEADER MODULES def create_godword(journal, date): @@ -403,6 +414,68 @@ def generate_default(block): def generate_info(block): return wrap_text(f'@info {block["value"]}') +### READ-ONLY STATS SECTION FUNCTIONS + +def generate_stats(page): + if not page['entries']: + return '' + + result = '' + + num_entries = len(page['entries']) + num_blocks = sum(len(entry['blocks']) for entry in page['entries']) + text_concat = ' '.join(b for e in page['entries'] for b in e['blocks'] if isinstance(b, str)) + num_words = len(text_concat.split()) + result += f'Entries: {num_entries}, Blocks: {num_blocks}, Words: {num_words}' + + last_entry = max(e['timestamp'] for e in page['entries']) + first_entry = min(e['timestamp'] for e in page['entries']) + entry_delta = last_entry - first_entry + entry_hours = round(entry_delta / 60 / 60, 2) + result += f'\nFirst: {format_timestamp(first_entry)}, Last: {format_timestamp(last_entry)}, Hours: {entry_hours}' + + calories = 0 + carbs = 0 + fat = 0 + protein = 0 + sugar = 0 + num_meals = 0 + first_meal = float('inf') + last_meal = float('-inf') + + foods, recipes = get_foods_file() + for entry in page['entries']: + did_count = False + for block in entry['blocks']: + if not isinstance(block, str) and block['type'] == 'diet': + food = evaluate_food_entry(foods, recipes, block['amount'], block['food']) + + if not did_count: + num_meals += 1 + first_meal = min(entry['timestamp'], first_meal) + last_meal = max(entry['timestamp'], last_meal) + did_count = True + + calories += food['Energy'] + carbs += food.get('Carbs', 0) + fat += food.get('Fat', 0) + protein += food.get('Protein', 0) + sugar += food.get('Sugar', 0) + + carbs_proportion = round(carbs * 4 / calories * 100) if carbs and calories else 0 + fat_proportion = round(fat * 9 / calories * 100) if fat and calories else 0 + protein_proportion = round(protein * 4 / calories * 100) if protein and calories else 0 + + calories, carbs, fat, protein, sugar = map(partial(round, ndigits=2), [calories, carbs, fat, protein, sugar]) + + meal_delta = last_meal - first_meal + meal_hours = round(meal_delta / 60 / 60, 2) + + result += f'\nCalories: {calories} ({carbs_proportion}/{fat_proportion}/{protein_proportion}, {protein}/{sugar}), Meals: {num_meals}, Hours: {meal_hours}' + + return result + + ### PAGE FUNCTIONS def create_header(journal, date): @@ -581,15 +654,20 @@ def generate_entry(entry): entry_modules = { 'diet': generate_diet, 'exercise': generate_exercise, - 'hide': lambda _: '@hide', - 'post': generate_post, - 'info': generate_info, - 'notes': generate_notes, 'behavior': generate_default, + + 'hide': lambda _: '@hide', + 'info': generate_info, + + 'post': generate_post, + + 'notes': generate_notes, + 'task': generate_default, 'start': generate_timer, 'stop': generate_timer, 'done': generate_timer, + 'notify': generate_notify, } @@ -638,6 +716,9 @@ def create_day(journal, date): } def parse_day(text): + # discard read-only QS section + text = text[text.find('#'):] + ENTRY_RE = re.compile(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ?', re.MULTILINE) header, *tmp = ENTRY_RE.split(text) @@ -653,7 +734,11 @@ def parse_day(text): } def generate_day(page): - result = f'# {page["title"]}' + result = '' + + result += generate_stats(page) + + result += f'\n\n# {page["title"]}' result += generate_header(page['header']) @@ -810,7 +895,69 @@ def handle_test(args): else: print('Test passed!') + def handle_summary(args): + def generate_food_summary(page): + result = '' + + def print(str=''): + nonlocal result + result += '\n' + str + + foods, recipes = get_foods_file() + + daily_calories = 0.0 + daily_protein = 0.0 + + for entry in page['entries']: + has_printed = False + entry_calories = 0.0 + entry_protein = 0.0 + for diet in (b for b in entry['blocks'] if type(b) != str and b['type'] == 'diet'): + if not has_printed: + print(f'-- {format_timestamp(entry["timestamp"])}') + has_printed = True + + value = diet['amount'] + name = diet['food'] + + 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: + print(f'ERROR: Invalid diet entry: {diet}') + 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)') + + return result + subcommand = nth_or_default(0, args, 'today') date = evaluate_time_expression(subcommand) @@ -822,62 +969,13 @@ def handle_summary(args): journal = load_journal() - foods, recipes = parse_foods_file(journal['files']['foods']) - - daily_grams = 0.0 - daily_calories = 0.0 - daily_protein = 0.0 - - for entry in journal['days'][date]['entries']: - has_printed = False - entry_calories = 0.0 - entry_protein = 0.0 - for diet in (b for b in entry['blocks'] if type(b) != str and b['type'] == 'diet'): - if not has_printed: - print(f'-- {format_timestamp(entry["timestamp"])}') - has_printed = True - - value = diet['amount'] - name = diet['food'] - - 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: - print(f'ERROR: Invalid diet entry: {diet}') - 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)') + print(generate_food_summary(journal['days'][date])) ### MAIN def main(): + init_hacky_hackery(load_journal()) + command = nth_or_default(1, sys.argv, 'open') args = sys.argv[2:]