gpn23-recipes/2_to_markdown.py

124 lines
3.9 KiB
Python
Raw Normal View History

2025-06-01 23:33:03 +02:00
import json
import sys
from pathlib import Path
from datetime import timedelta
import re
from pathvalidate import validate_filename
import os
2025-06-02 01:38:40 +02:00
from tqdm import tqdm
2025-06-01 23:33:03 +02:00
2025-06-02 01:37:39 +02:00
include_title = False
2025-06-02 00:45:10 +02:00
output_dir = "recipes-md"
2025-06-02 01:38:40 +02:00
2025-06-02 00:45:10 +02:00
def valid_duration(duration):
if not duration:
return False
if duration == "0":
return False
return True
2025-06-02 01:38:04 +02:00
def valid_description(description):
if not description:
return False
return True
2025-06-01 23:33:03 +02:00
def parse_duration(duration):
if not duration:
return ""
match = re.match(r"PT(?:(\d+)H)?(?:(\d+)M)?", duration)
if not match:
return duration
hours, minutes = match.groups()
hours = f"{int(hours)}h " if hours else ""
minutes = f"{int(minutes)}m" if minutes else ""
return f"{hours}{minutes}".strip()
def format_recipe_to_markdown(recipe):
md = []
2025-06-02 01:37:39 +02:00
# Title (may be implicit by filename)
if include_title:
md.append(f"# {recipe.get('name', 'Untitled Recipe')}")
2025-06-01 23:33:03 +02:00
# Description
2025-06-02 01:38:04 +02:00
if valid_description(recipe.get('description')):
2025-06-01 23:33:03 +02:00
md.append(f"\n_{recipe['description']}_\n")
2025-06-02 00:45:10 +02:00
# Details
details_parts = []
if valid_duration(recipe.get('prepTime')):
details_parts.append(f"Prep time: {parse_duration(recipe['prepTime'])}")
if valid_duration(recipe.get('cookTime')):
details_parts.append(f"Cook time: {parse_duration(recipe['cookTime'])}")
if valid_duration(recipe.get('totalTime')):
details_parts.append(f"Total time: {parse_duration(recipe['totalTime'])}")
2025-06-01 23:33:03 +02:00
if 'recipeYield' in recipe:
2025-06-02 00:45:10 +02:00
details_parts.append(f"Portionen: {recipe['recipeYield']}")
if details_parts:
for p in details_parts:
md.append(f"* {p}\n")
2025-06-01 23:33:03 +02:00
# Ingredients
if 'recipeIngredient' in recipe:
2025-06-02 00:45:10 +02:00
md.append("\n## Zutaten")
2025-06-01 23:33:03 +02:00
for ingredient in recipe['recipeIngredient']:
md.append(f"- {ingredient}")
# Instructions
if 'recipeInstructions' in recipe:
2025-06-02 00:45:10 +02:00
md.append("\n## Zubereitung")
2025-06-01 23:33:03 +02:00
instructions = recipe['recipeInstructions']
if isinstance(instructions, list):
for i, step in enumerate(instructions, 1):
if isinstance(step, dict):
text = step.get('text', '').strip()
else:
text = str(step).strip()
md.append(f"{i}. {text.replace(chr(10), ' ')}")
else:
for i, line in enumerate(instructions.strip().split('\n'), 1):
if line.strip():
md.append(f"{i}. {line.strip()}")
# Nutrition
if 'nutrition' in recipe:
md.append("\n## Nutrition")
for key, value in recipe['nutrition'].items():
if key != "@type":
md.append(f"- **{key.replace('_', ' ').capitalize()}**: {value}")
return '\n'.join(md)
def main(json_file):
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# If the JSON-LD is embedded in @graph or a list
# if isinstance(data, list):
# recipe = next((item for item in data if item.get('@type') == 'Recipe'), data[0])
# elif '@graph' in data:
# recipe = next((item for item in data['@graph'] if item.get('@type') == 'Recipe'), data['@graph'][0])
if isinstance(data, list) and data[0].get('@type') == 'Recipe':
recipes = data
elif data.get('@type') == 'Recipe':
recipes = [data]
else:
print("No Recipe object found in JSON.")
return
2025-06-02 01:38:40 +02:00
for recipe in tqdm(recipes):
2025-06-01 23:33:03 +02:00
markdown_fn = f"{recipe.get('name', 'Untitled Recipe')}.md"
validate_filename(markdown_fn) # XXX does this check directory traversal?
markdown = format_recipe_to_markdown(recipe)
2025-06-02 00:45:10 +02:00
os.makedirs(output_dir, exist_ok=True)
with open(os.path.join(output_dir, markdown_fn), "w") as f:
2025-06-01 23:33:03 +02:00
f.write(markdown)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python recipe_to_md.py <path_to_recipe_json>")
else:
main(sys.argv[1])