Skip to content

Instantly share code, notes, and snippets.

@jachin
Created February 15, 2024 03:02
Show Gist options
  • 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}")
@pmstick
Copy link

pmstick commented May 21, 2024

Thanks! This worked great for me. Mealie is so much nicer than OpenEats.

@boyturtle2
Copy link

Thanks, this worked for me too

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