mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
backup improvements
This commit is contained in:
parent
db294e888f
commit
400fbac7b9
14 changed files with 249 additions and 173 deletions
|
@ -121,10 +121,9 @@ export default {
|
|||
async createBackup() {
|
||||
this.backupLoading = true;
|
||||
|
||||
let response = await api.backups.create(
|
||||
this.backupTag,
|
||||
this.selectedTemplate
|
||||
);
|
||||
let response = await api.backups.create(this.backupTag, [
|
||||
this.selectedTemplate,
|
||||
]);
|
||||
|
||||
if (response.status == 201) {
|
||||
this.selectedBackup = null;
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Recipe",
|
||||
"articleBody": "\u201cAfter a draining day juggling work, homeschooling, and urging children to stop using their masks as slingshots, the ideal food for me isn\u2019t perfectly prepared food that\u2019s been tweezered into position, but a meal that\u2019s simply comforting,\u201d writes the Smitten Kitchen\u2019s Deb Perelman. Right now, it\u2019s this deeply cozy pot of tender chicken thighs, jammy leeks, and broth-soaked rice.",
|
||||
"alternativeHeadline": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.",
|
||||
"dateModified": "2021-01-10 15:20:51.422000",
|
||||
"datePublished": "2020-08-18 04:00:00",
|
||||
"articleBody": "Leftover rice is ideal for this dish (and a great way to use up any takeout that\u2019s hanging around), since fully chilled rice tends to be drier and will become crispier and browner in the skillet. To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it out like a pancake. Don\u2019t touch until you hear it crackle! Finish with a sunny-side-up egg\u2014or poach it if you don't mind the stovetop fuss. This recipe is part of the 2021\u00a0Feel Good Food Plan, our eight-day dinner plan for starting the year off right.",
|
||||
"alternativeHeadline": "To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it. Don\u2019t touch until you hear it crackle!",
|
||||
"dateModified": "2021-01-11 23:25:22.997000",
|
||||
"datePublished": "2021-01-01 06:00:00",
|
||||
"keywords": [
|
||||
"recipes",
|
||||
"chicken recipes",
|
||||
"kosher salt",
|
||||
"black pepper",
|
||||
"butter",
|
||||
"leek",
|
||||
"lemon zest",
|
||||
"rice",
|
||||
"chicken broth",
|
||||
"anchovy",
|
||||
"garlic",
|
||||
"capers",
|
||||
"herb",
|
||||
"olive oil",
|
||||
"healthyish",
|
||||
"salad",
|
||||
"ginger",
|
||||
"garlic",
|
||||
"orange",
|
||||
"oil",
|
||||
"soy sauce",
|
||||
"lemon juice",
|
||||
"sesame oil",
|
||||
"kosher salt",
|
||||
"broccoli",
|
||||
"brown rice",
|
||||
"egg",
|
||||
"celery",
|
||||
"cilantro",
|
||||
"mint",
|
||||
"feel good food plan 2021",
|
||||
"feel good food plan",
|
||||
"web"
|
||||
],
|
||||
"thumbnailUrl": "https://assets.bonappetit.com/photos/5f29796456f43685a49327fb/1:1/w_1125,h_1125,c_limit/Chicken-and-Rice-With-Leeks-Salsa-Verde-01.jpg",
|
||||
"thumbnailUrl": "https://assets.bonappetit.com/photos/5fdbe70a84d333dd1dcc7900/1:1/w_1698,h_1698,c_limit/BA1220feelgoodalt.jpg",
|
||||
"publisher": {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
|
@ -47,49 +51,68 @@
|
|||
"author": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Deb Perelman",
|
||||
"sameAs": "https://bon-appetit.com/contributor/deb-perelman/"
|
||||
"name": "Devonn Francis",
|
||||
"sameAs": "https://bon-appetit.com/contributor/devonn-francis/"
|
||||
}
|
||||
],
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": 4.02,
|
||||
"ratingCount": 48
|
||||
"ratingValue": 4.33,
|
||||
"ratingCount": 33
|
||||
},
|
||||
"description": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.",
|
||||
"image": "chicken-and-rice-with-leeks-and-salsa-verde.jpg",
|
||||
"headline": "Chicken and Rice With Leeks and Salsa Verde",
|
||||
"name": "Chicken and Rice With Leeks and Salsa Verde",
|
||||
"description": "To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it. Don\u2019t touch until you hear it crackle! ",
|
||||
"image": "crispy-rice-with-ginger-citrus-celery-salad.jpg",
|
||||
"headline": "Crispy Rice With Ginger-Citrus Celery Salad",
|
||||
"name": "Crispy Rice With Ginger-Citrus Celery Salad",
|
||||
"recipeIngredient": [
|
||||
"1\u00bd lb. skinless, boneless chicken thighs (4\u20138 depending on size)",
|
||||
"Kosher salt, freshly ground pepper",
|
||||
"3 Tbsp. unsalted butter, divided",
|
||||
"2 large or 3 medium leeks, white and pale green parts only, halved lengthwise, thinly sliced",
|
||||
"Zest and juice of 1 lemon, divided",
|
||||
"1\u00bd cups long-grain white rice, rinsed until water runs clear",
|
||||
"2\u00be cups low-sodium chicken broth",
|
||||
"1 oil-packed anchovy fillet",
|
||||
"2 garlic cloves",
|
||||
"1 Tbsp. drained capers",
|
||||
"Crushed red pepper flakes",
|
||||
"1 cup tender herb leaves (such as parsley, cilantro, and/or mint)",
|
||||
"4\u20135 Tbsp. extra-virgin olive oil"
|
||||
"1 2\" piece ginger, peeled, finely grated",
|
||||
"1 small garlic clove, finely grated",
|
||||
"Juice of 1 orange",
|
||||
"2 tbsp. vegetable oil",
|
||||
"1Tbsp. coconut aminos or low-sodium soy sauce",
|
||||
"1 Tbsp. fresh lemon juice",
|
||||
"\u00bc tsp. toasted sesame oil",
|
||||
"Kosher salt",
|
||||
"1 medium head of broccoli",
|
||||
"6 Tbsp. (or more) vegetable oil, divided",
|
||||
"Kosher salt",
|
||||
"2 cups chilled cooked brown rice",
|
||||
"4 large eggs",
|
||||
"3 celery stalks, thinly sliced on a steep diagonal",
|
||||
"\u00bd cup cilantro leaves with tender stems",
|
||||
"\u00bd cup mint leaves",
|
||||
"Crushed red pepper flakes (for serving)"
|
||||
],
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"text": "Season chicken with salt and pepper. Melt 2 Tbsp. butter in a large high-sided skillet over medium-high heat. Add leeks and half of lemon zest, season with salt and pepper, and mix to coat leeks in butter. Reduce heat to medium-low, cover, and cook, stirring occasionally, until leeks are somewhat tender, about 5 minutes. Remove lid, increase heat to medium-high, and cook, stirring occasionally, until tender and just starting to take on color, about 3 minutes. Add rice and cook, stirring often, 3 minutes, then add broth, scraping up any browned bits. Tuck short sides of each chicken thigh underneath so they are touching and nestle seam side down into rice mixture. Bring to a simmer. Cover, reduce heat to medium-low, and cook until rice is tender and chicken is cooked through, about 20 minutes. Remove from heat. Cut remaining 1 Tbsp. butter into small pieces and scatter over mixture. Re-cover and let sit 10 minutes."
|
||||
"text": "Whisk ginger, garlic, orange juice, vegetable oil, coconut aminos, lemon juice, and sesame oil in a small bowl; season with salt and set aside."
|
||||
},
|
||||
{
|
||||
"text": "Meanwhile, pulse anchovy, garlic, capers, a few pinches of red pepper flakes, and remaining lemon zest in a food processor until finely chopped. Add herbs; process until a paste forms. With motor running, gradually stream in oil until loosened to a thick sauce. Add half of lemon juice; season salsa verde with salt."
|
||||
"text": "Trim about \u00bd\" from woody end of broccoli stem. Peel tough outer layer from stem. Cut florets from stems and thinly slice stems about \u00bd\" thick. Break florets apart with your hands into 1\"\u20131\u00bd\" pieces."
|
||||
},
|
||||
{
|
||||
"text": "Drizzle remaining lemon juice over chicken and rice. Serve with salsa verde."
|
||||
"text": "Heat 2 Tbsp. oil in a large nonstick skillet over medium. Working in 2 batches if needed, arrange broccoli in a single layer and cook, tossing occasionally, until broccoli is bright green and lightly charred around the edges, about\u00a03 minutes. Transfer to a large plate."
|
||||
},
|
||||
{
|
||||
"text": "Pour 2 Tbsp. oil into same pan and heat over medium-high. Once you see the first wisp of smoke, add rice and season lightly with salt. Using a spatula or spoon, press rice evenly into pan like a pancake. Rice will begin to crackle, but don\u2019t fuss with it. When the crackling has died down almost completely, about\u00a03 minutes, break rice into large pieces and turn over."
|
||||
},
|
||||
{
|
||||
"text": "Add broccoli back to pan and give everything a toss to combine. Cook, tossing occasionally and adding another\u00a01 Tbsp. oil if pan looks dry, until broccoli is tender and rice is warmed through and very crisp, about 5 minutes. Transfer mixture to a platter or divide among plates and set aside."
|
||||
},
|
||||
{
|
||||
"text": "Wipe out skillet; heat remaining\u00a02 Tbsp. oil over medium-high. Crack eggs into skillet; season with salt. Oil should bubble around eggs right away. Cook, rotating skillet occasionally, until whites are golden brown and crisp at the edges and set around the yolk (which should be runny), about 2 minutes."
|
||||
},
|
||||
{
|
||||
"text": "Toss celery, cilantro, and mint with\u00a03 Tbsp. reserved dressing and a pinch of salt in a medium bowl to combine."
|
||||
},
|
||||
{
|
||||
"text": "Scatter celery salad over fried rice; top with fried eggs and sprinkle with red pepper flakes. Serve extra dressing alongside."
|
||||
}
|
||||
],
|
||||
"recipeYield": "4 Servings",
|
||||
"url": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"slug": "chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"orgURL": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"recipeYield": "4 servings",
|
||||
"url": "https://www.bonappetit.com/recipe/crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"slug": "crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"orgURL": "https://www.bonappetit.com/recipe/crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"dateAdded": null,
|
||||
|
|
|
@ -13,7 +13,9 @@ class BaseDocument:
|
|||
self.document: mongoengine.Document
|
||||
|
||||
@staticmethod
|
||||
def _unpack_mongo(document) -> dict: # TODO: Probably Put a version in each class to speed up reads?
|
||||
def _unpack_mongo(
|
||||
document,
|
||||
) -> dict: # TODO: Probably Put a version in each class to speed up reads?
|
||||
document = json.loads(document.to_json())
|
||||
del document["_id"]
|
||||
|
||||
|
@ -41,19 +43,19 @@ class BaseDocument:
|
|||
except:
|
||||
pass
|
||||
|
||||
|
||||
return document
|
||||
|
||||
def get_all(self, limit: int = None, order_by: str = "dateAdded"):
|
||||
def get_all(self, limit: int = None, order_by: str = None):
|
||||
if USE_MONGO:
|
||||
if order_by:
|
||||
documents = self.document.objects.order_by(str(order_by)).limit(limit)
|
||||
elif limit == None:
|
||||
documents = self.document.objects()
|
||||
else:
|
||||
documents = self.document.objects().limit(limit)
|
||||
docs = []
|
||||
for item in documents:
|
||||
doc = BaseDocument._unpack_mongo(item)
|
||||
docs.append(doc)
|
||||
|
||||
docs = [BaseDocument._unpack_mongo(item) for item in documents]
|
||||
|
||||
if limit == 1:
|
||||
return docs[0]
|
||||
return docs
|
||||
|
@ -74,7 +76,7 @@ class BaseDocument:
|
|||
limit (int, optional): A limit to returned responses. Defaults to 1. \n
|
||||
|
||||
Returns:
|
||||
dict or list[dict]:
|
||||
dict or list[dict]:
|
||||
"""
|
||||
if match_key == None:
|
||||
match_key = self.primary_key
|
||||
|
|
|
@ -25,7 +25,7 @@ class _Themes(BaseDocument):
|
|||
def update(self, data: dict) -> dict:
|
||||
if USE_MONGO:
|
||||
colors = ThemeColorsDocument(**data["colors"])
|
||||
theme_document = SiteThemeDocument.objects.get(name=data.get("name"))
|
||||
theme_document = self.document.objects.get(name=data.get("name"))
|
||||
|
||||
if theme_document:
|
||||
theme_document.update(set__colors=colors)
|
||||
|
|
|
@ -5,7 +5,7 @@ from pydantic import BaseModel
|
|||
|
||||
class BackupJob(BaseModel):
|
||||
tag: Optional[str]
|
||||
template: Optional[str]
|
||||
template: Optional[List[str]]
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
from fastapi import APIRouter, HTTPException
|
||||
from models.backup_models import BackupJob, Imports
|
||||
from services.backup_services import (
|
||||
BACKUP_DIR,
|
||||
TEMPLATE_DIR,
|
||||
export_db,
|
||||
import_from_archive,
|
||||
)
|
||||
from services.backups.export import backup_all
|
||||
from settings import BACKUP_DIR, TEMPLATE_DIR
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
@ -28,17 +24,15 @@ async def available_imports():
|
|||
@router.post("/api/backups/export/database/", tags=["Import / Export"], status_code=201)
|
||||
async def export_database(data: BackupJob):
|
||||
"""Generates a backup of the recipe database in json format."""
|
||||
|
||||
export_path = backup_all(data.tag, data.template)
|
||||
try:
|
||||
export_path = export_db(data.tag, data.template)
|
||||
return SnackResponse.success("Backup Created at " + export_path)
|
||||
except:
|
||||
HTTPException(
|
||||
status_code=400,
|
||||
detail=SnackResponse.error("Error Creating Backup. See Log File"),
|
||||
)
|
||||
|
||||
return SnackResponse.success("Backup Created at " + export_path)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/api/backups/{file_name}/import/", tags=["Import / Export"], status_code=200
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
from db.tinydb.tinydb_setup import TinyDatabase
|
||||
from services.backups.export import export_recipes
|
||||
|
||||
db = TinyDatabase()
|
||||
|
||||
test_object = {"name": "dan", "job": "programmer"}
|
||||
test_object_2 = {"name": "jennifer", "job": "programmer"}
|
||||
test_object_3 = {"name": "steve", "job": "programmer"}
|
||||
|
||||
|
||||
db.recipes.delete("jennifer")
|
||||
db.recipes.delete("dan")
|
||||
db.recipes.save(test_object)
|
||||
db.recipes.save(test_object_2)
|
||||
db.recipes.update_doc("dan", test_object_3)
|
||||
export_recipes()
|
|
@ -1,11 +1,9 @@
|
|||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from db.mongo.recipe_models import RecipeDocument
|
||||
from jinja2 import Template
|
||||
from utils.logger import logger
|
||||
|
||||
from services.recipe_services import IMG_DIR
|
||||
|
@ -16,18 +14,6 @@ TEMPLATE_DIR = CWD.parent.joinpath("data", "templates")
|
|||
TEMP_DIR = CWD.parent.joinpath("data", "temp")
|
||||
|
||||
|
||||
def auto_backup_job():
|
||||
for backup in BACKUP_DIR.glob("Auto*.zip"):
|
||||
backup.unlink()
|
||||
|
||||
templates = []
|
||||
for template in TEMPLATE_DIR.iterdir():
|
||||
templates.append(template)
|
||||
|
||||
export_db(tag="Auto", templates=templates)
|
||||
logger.info("Auto Backup Called")
|
||||
|
||||
|
||||
def import_migration(recipe_dict: dict) -> dict:
|
||||
del recipe_dict["_id"]
|
||||
del recipe_dict["dateAdded"]
|
||||
|
@ -72,78 +58,5 @@ def import_from_archive(file_name: str) -> list:
|
|||
return {"successful": successful_imports, "failed": failed_imports}
|
||||
|
||||
|
||||
def export_db(tag=None, templates=None):
|
||||
if tag:
|
||||
export_tag = tag + "_" + datetime.now().strftime("%Y-%b-%d")
|
||||
else:
|
||||
export_tag = datetime.now().strftime("%Y-%b-%d")
|
||||
|
||||
backup_folder = TEMP_DIR.joinpath(export_tag)
|
||||
backup_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
img_folder = backup_folder.joinpath("images")
|
||||
img_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
recipe_folder = backup_folder.joinpath("recipes")
|
||||
recipe_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
export_images(img_folder)
|
||||
|
||||
if type(templates) == list:
|
||||
for template in templates:
|
||||
export_recipes(recipe_folder, template)
|
||||
elif type(templates) == str:
|
||||
export_recipes(recipe_folder, templates)
|
||||
else:
|
||||
export_recipes(recipe_folder)
|
||||
|
||||
zip_path = BACKUP_DIR.joinpath(f"{export_tag}")
|
||||
shutil.make_archive(zip_path, "zip", backup_folder)
|
||||
|
||||
shutil.rmtree(backup_folder)
|
||||
shutil.rmtree(TEMP_DIR)
|
||||
|
||||
return str(zip_path.absolute()) + ".zip"
|
||||
|
||||
|
||||
def export_images(dest_dir) -> Path:
|
||||
for file in IMG_DIR.iterdir():
|
||||
shutil.copy(file, dest_dir.joinpath(file.name))
|
||||
|
||||
|
||||
def export_recipes(dest_dir: Path, template=None) -> Path:
|
||||
all_recipes = RecipeDocument.objects()
|
||||
logger.info(f"Backing Up Recipes: {all_recipes}")
|
||||
for recipe in all_recipes:
|
||||
json_recipe = recipe.to_json(indent=4)
|
||||
|
||||
if template:
|
||||
md_dest = dest_dir.parent.joinpath("templates")
|
||||
md_dest.mkdir(parents=True, exist_ok=True)
|
||||
template = TEMPLATE_DIR.joinpath(template)
|
||||
export_markdown(md_dest, json_recipe, template)
|
||||
|
||||
filename = recipe.slug + ".json"
|
||||
file_path = dest_dir.joinpath(filename)
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
f.write(json_recipe)
|
||||
|
||||
|
||||
def export_markdown(dest_dir: Path, recipe_data: json, template=Path) -> Path:
|
||||
recipe_data: dict = json.loads(recipe_data)
|
||||
recipe_template = TEMPLATE_DIR.joinpath("recipes.md")
|
||||
|
||||
with open(recipe_template, "r") as f:
|
||||
template = Template(f.read())
|
||||
|
||||
out_file = dest_dir.joinpath(recipe_data["slug"] + ".md")
|
||||
|
||||
content = template.render(recipe=recipe_data)
|
||||
|
||||
with open(out_file, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Template
|
||||
from services.meal_services import MealPlan
|
||||
from services.recipe_services import Recipe
|
||||
from services.settings_services import SiteSettings, SiteTheme
|
||||
from settings import BACKUP_DIR, IMG_DIR, TEMP_DIR, TEMPLATE_DIR
|
||||
from utils.logger import logger
|
||||
|
||||
|
||||
class DatabaseExport:
|
||||
def __init__(self, tag=None, templates=None) -> None:
|
||||
if tag:
|
||||
export_tag = tag + "_" + datetime.now().strftime("%Y-%b-%d")
|
||||
else:
|
||||
export_tag = datetime.now().strftime("%Y-%b-%d")
|
||||
|
||||
self.main_dir = TEMP_DIR.joinpath(export_tag)
|
||||
self.img_dir = self.main_dir.joinpath("images")
|
||||
self.recipe_dir = self.main_dir.joinpath("recipes")
|
||||
self.themes_dir = self.main_dir.joinpath("themes")
|
||||
self.settings_dir = self.main_dir.joinpath("settings")
|
||||
self.templates_dir = self.main_dir.joinpath("templates")
|
||||
self.mealplans_dir = self.main_dir.joinpath("mealplans")
|
||||
|
||||
try:
|
||||
self.templates = [TEMPLATE_DIR.joinpath(x) for x in templates]
|
||||
except:
|
||||
self.templates = False
|
||||
logger.info("No Jinja2 Templates Registered for Export")
|
||||
|
||||
required_dirs = [
|
||||
self.main_dir,
|
||||
self.img_dir,
|
||||
self.recipe_dir,
|
||||
self.themes_dir,
|
||||
self.settings_dir,
|
||||
self.templates_dir,
|
||||
self.mealplans_dir,
|
||||
]
|
||||
|
||||
for dir in required_dirs:
|
||||
dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def export_recipes(self):
|
||||
all_recipes = Recipe.get_all()
|
||||
|
||||
for recipe in all_recipes:
|
||||
logger.info(f"Backing Up Recipes: {recipe}")
|
||||
|
||||
filename = recipe.get("slug") + ".json"
|
||||
file_path = self.recipe_dir.joinpath(filename)
|
||||
|
||||
DatabaseExport._write_json_file(recipe, file_path)
|
||||
|
||||
if self.templates:
|
||||
self._export_template(recipe)
|
||||
|
||||
def _export_template(self, recipe_data: dict):
|
||||
for template_path in self.templates:
|
||||
|
||||
with open(template_path, "r") as f:
|
||||
template = Template(f.read())
|
||||
|
||||
filename = recipe_data.get("name") + template_path.suffix
|
||||
out_file = self.templates_dir.joinpath(filename)
|
||||
|
||||
content = template.render(recipe=recipe_data)
|
||||
|
||||
with open(out_file, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
def export_images(self):
|
||||
for file in IMG_DIR.iterdir():
|
||||
shutil.copy(file, self.img_dir.joinpath(file.name))
|
||||
|
||||
def export_settings(self):
|
||||
all_settings = SiteSettings.get_site_settings()
|
||||
out_file = self.settings_dir.joinpath("settings.json")
|
||||
DatabaseExport._write_json_file(all_settings.dict(), out_file)
|
||||
|
||||
def export_themes(self):
|
||||
all_themes = SiteTheme.get_all()
|
||||
print(all_themes)
|
||||
if all_themes:
|
||||
all_themes = [x.dict() for x in all_themes]
|
||||
out_file = self.themes_dir.joinpath("themes.json")
|
||||
DatabaseExport._write_json_file(all_themes, out_file)
|
||||
|
||||
# def export_meals(self): #! Problem Parseing Datetime Objects... May come back to this
|
||||
# meal_plans = MealPlan.get_all()
|
||||
# if meal_plans:
|
||||
# meal_plans = [x.dict() for x in meal_plans]
|
||||
|
||||
# print(meal_plans)
|
||||
# out_file = self.mealplans_dir.joinpath("mealplans.json")
|
||||
# DatabaseExport._write_json_file(meal_plans, out_file)
|
||||
|
||||
@staticmethod
|
||||
def _write_json_file(data, out_file: Path):
|
||||
json_data = json.dumps(data, indent=4)
|
||||
|
||||
with open(out_file, "w") as f:
|
||||
f.write(json_data)
|
||||
|
||||
def finish_export(self):
|
||||
zip_path = BACKUP_DIR.joinpath(f"{self.main_dir.name}")
|
||||
shutil.make_archive(zip_path, "zip", self.main_dir)
|
||||
|
||||
shutil.rmtree(TEMP_DIR)
|
||||
|
||||
return str(zip_path.absolute()) + ".zip"
|
||||
|
||||
|
||||
def backup_all(tag=None, templates=None):
|
||||
db_export = DatabaseExport(tag=tag, templates=templates)
|
||||
|
||||
db_export.export_recipes()
|
||||
db_export.export_images()
|
||||
db_export.export_settings()
|
||||
db_export.export_themes()
|
||||
# db_export.export_meals()
|
||||
|
||||
return db_export.finish_export()
|
||||
|
||||
|
||||
def auto_backup_job():
|
||||
for backup in BACKUP_DIR.glob("Auto*.zip"):
|
||||
backup.unlink()
|
||||
|
||||
templates = []
|
||||
for template in TEMPLATE_DIR.iterdir():
|
||||
templates.append(template)
|
||||
|
||||
backup_all(tag="Auto", templates=templates)
|
||||
logger.info("Auto Backup Called")
|
|
@ -0,0 +1,9 @@
|
|||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from utils.logger import logger
|
||||
|
||||
from settings import IMG_DIR, BACKUP_DIR, TEMPLATE_DIR, TEMP_DIR
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import json
|
||||
from datetime import date, timedelta
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
|
|
@ -134,6 +134,10 @@ class Recipe(BaseModel):
|
|||
def update_image(slug: str, extension: str):
|
||||
db.recipes.update_image(slug, extension)
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
return db.recipes.get_all()
|
||||
|
||||
|
||||
def read_requested_values(keys: list, max_results: int = 0) -> List[dict]:
|
||||
"""
|
||||
|
|
|
@ -5,7 +5,7 @@ import requests
|
|||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from utils.logger import logger
|
||||
|
||||
from services.backup_services import auto_backup_job
|
||||
from services.backups.export import auto_backup_job
|
||||
from services.meal_services import MealPlan
|
||||
from services.recipe_services import Recipe
|
||||
from services.settings_services import SiteSettings
|
||||
|
|
|
@ -29,6 +29,10 @@ class SiteSettings(BaseModel):
|
|||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
db.settings.get_all()
|
||||
|
||||
@classmethod
|
||||
def get_site_settings(cls):
|
||||
try:
|
||||
|
@ -137,4 +141,4 @@ def default_theme_init():
|
|||
default_theme.save_to_db()
|
||||
|
||||
|
||||
# default_theme_init()
|
||||
default_theme_init()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue