Skip to content

Instantly share code, notes, and snippets.

@jachin
Created February 15, 2024 03:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jachin/508fe1b7b2717faa1da5a84ae793b027 to your computer and use it in GitHub Desktop.
Save jachin/508fe1b7b2717faa1da5a84ae793b027 to your computer and use it in GitHub Desktop.
OpenEats to Nextcloud Cookbook
import requests
import os
import json
import getpass
import datetime
from shutil import copyfileobj, make_archive
from urllib.parse import urljoin
from pathlib import Path
# Configuration
openeats_base_url = input(
"Enter your OpenEats url (eg: https://openeats.example.com): "
)
login_endpoint = (
"/api/v1/accounts/obtain-auth-token/" # Or the specific endpoint used for login
)
output_directory = "nextcloud_cookbook_recipes" # Directory to save JSON files
# Prompt for username and password
username = input("Enter your OpenEats username: ")
password = getpass.getpass("Enter your OpenEats password: ")
# Start a session
session = requests.Session()
# Login function to obtain a session cookie
def login(base_url, endpoint, user, pwd):
url = base_url + endpoint
login_data = {"username": user, "password": pwd}
response = session.post(url, data=login_data)
response.raise_for_status() # Raises an error if the login failed
# The session is now authenticated; the session cookies contain the auth token
# Fetch recipes using authenticated session
def get_openeats_recipes():
api_url = f"{openeats_base_url}/api/v1/recipe/recipes/"
response = session.get(api_url)
response.raise_for_status()
return response.json()["results"]
# Helper function to format time
def format_duration(minutes):
if not minutes:
return ""
return f"PT{minutes}M"
# Function to download an image and return the saved path
def download_image(image_url, save_as):
if not image_url:
return None
response = requests.get(image_url, stream=True)
if response.status_code == 200:
with open(save_as, "wb") as f:
copyfileobj(response.raw, f)
return save_as
return None
# Function to convert an OpenEats recipe to the Nextcloud Cookbook format
def convert_to_nextcloud_cookbook_format(openeats_recipe):
# Assuming OpenEats uses minutes as integers for prep_time and cook_time
prep_time = format_duration(openeats_recipe.get("prep_time"))
cook_time = format_duration(openeats_recipe.get("cook_time"))
total_time = format_duration(
openeats_recipe.get("prep_time") + openeats_recipe.get("cook_time")
)
ingredients = []
for group in openeats_recipe["ingredient_groups"]:
group_title = group.get("title", "").strip()
for ingredient in group["ingredients"]:
quantity = (
f"{ingredient['numerator']}/{ingredient['denominator']}"
if ingredient["denominator"] > 1
else f"{ingredient['numerator']}"
)
ingredient_str = (
f"{quantity} {ingredient['measurement']} {ingredient['title']}"
)
if group_title:
ingredient_str = f"{group_title}: {ingredient_str}"
ingredients.append(ingredient_str)
instructions = [
{"@type": "HowToStep", "text": step}
for step in openeats_recipe["directions"].split("\n")
if step.strip()
]
# Format the date as a full-date (YYYY-MM-DD)
publish_date = (
datetime.datetime.strptime(openeats_recipe["pub_date"], "%Y-%m-%d")
.date()
.isoformat()
)
return {
"name": openeats_recipe["title"],
"description": openeats_recipe.get("info", ""),
"prepTime": prep_time,
"cookTime": cook_time,
"totalTime": total_time,
"recipeYield": openeats_recipe["servings"],
"image": openeats_recipe.get("photo", ""),
"recipeIngredient": ingredients,
"recipeInstructions": instructions,
"author": {"name": openeats_recipe["username"]},
"datePublished": publish_date,
"keywords": [tag["title"] for tag in openeats_recipe["tags"]],
"recipeCategory": openeats_recipe["course"]["title"],
"recipeCuisine": openeats_recipe["cuisine"]["title"],
# Additional fields here if applicable
}
# Function to save recipe as a JSON file
def save_recipe_as_json(recipe, directory):
filename = f"{recipe['name'].replace(' ', '_')}.json"
filepath = os.path.join(directory, filename)
with open(filepath, "w", encoding="utf-8") as f:
json.dump(recipe, f, ensure_ascii=False, indent=4)
# Function to convert an OpenEats recipe to the Nextcloud Cookbook format
# and download images to the recipe folder
def process_recipe(openeats_recipe, output_directory):
# Create a recipe folder
safe_title = openeats_recipe["title"].replace(" ", "_").replace("/", "_")
recipe_dir = Path(output_directory) / safe_title
recipe_dir.mkdir(parents=True, exist_ok=True)
# Download main photo
if openeats_recipe.get("photo"):
full_image_path = recipe_dir / "full.jpg"
download_image(
urljoin(openeats_base_url, openeats_recipe["photo"]), full_image_path
)
# Download thumbnail photo (if different)
if (
openeats_recipe.get("photo_thumbnail")
and openeats_recipe["photo_thumbnail"] != openeats_recipe["photo"]
):
thumb_image_path = recipe_dir / "thumb.jpg"
download_image(
urljoin(openeats_base_url, openeats_recipe["photo_thumbnail"]),
thumb_image_path,
)
# Construct Nextcloud recipe JSON
nextcloud_recipe = convert_to_nextcloud_cookbook_format(openeats_recipe)
# Save Nextcloud recipe JSON file
recipe_json_path = recipe_dir / "recipe.json"
with open(recipe_json_path, "w", encoding="utf-8") as f:
json.dump(nextcloud_recipe, f, ensure_ascii=False, indent=4)
return recipe_dir
# Function to zip the directory of recipes
def zip_recipes(directory, zip_name):
make_archive(zip_name, "zip", directory)
# Main script execution
if __name__ == "__main__":
# Create output directory if it does not exist
if not os.path.exists(output_directory):
os.makedirs(output_directory)
try:
# Perform login
login(openeats_base_url, login_endpoint, username, password)
# Fetch recipes from OpenEats
openeats_recipes = get_openeats_recipes()
# Convert and save each recipe and its associated images
for openeats_recipe in openeats_recipes:
recipe_dir = process_recipe(openeats_recipe, output_directory)
print(f"Recipe saved in: {recipe_dir}")
# Compress the recipes directory into a zip file
zip_recipes(output_directory, "nextcloud")
print(f"Recipes have been zipped into nextcloud.zip")
except requests.RequestException as error:
print(f"An error occurred: {error}")
@jachin
Copy link
Author

jachin commented Feb 15, 2024

I wrote this (with some help from ChatGTP) export all my recipes in OpenEats to a format that Mealie can import.

I suppose it would be easy to imagine you could also import your recipies into the Nextcloud Cookbook app, but I haven't actually tried that.

It's also not perfect, there's probably some cleanup you'll need to do afterwards. Mostly around cleaning up the directions. If anyone wants to help make it better, please do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment