mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
potentially fix #217
This commit is contained in:
parent
e25266c089
commit
802633a060
6 changed files with 68 additions and 117 deletions
|
@ -13,43 +13,13 @@
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-card-text class="mb-n4">
|
<v-card-text class="mb-n4">
|
||||||
<v-row>
|
<v-row>
|
||||||
<div>
|
<div v-for="values in allNumbers" :key="values.title">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<div>
|
<div>
|
||||||
<h3>Recipes</h3>
|
<h3>{{ values.title }}</h3>
|
||||||
</div>
|
|
||||||
<div class="success--text">
|
|
||||||
Success: {{ recipeNumbers.success }}
|
|
||||||
</div>
|
|
||||||
<div class="error--text">
|
|
||||||
Failed: {{ recipeNumbers.failure }}
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<v-card-text>
|
|
||||||
<div>
|
|
||||||
<h3>Themes</h3>
|
|
||||||
</div>
|
|
||||||
<div class="success--text">
|
|
||||||
Success: {{ themeNumbers.success }}
|
|
||||||
</div>
|
|
||||||
<div class="error--text">
|
|
||||||
Failed: {{ themeNumbers.failure }}
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<v-card-text>
|
|
||||||
<div>
|
|
||||||
<h3>Settings</h3>
|
|
||||||
</div>
|
|
||||||
<div class="success--text">
|
|
||||||
Success: {{ settingsNumbers.success }}
|
|
||||||
</div>
|
|
||||||
<div class="error--text">
|
|
||||||
Failed: {{ settingsNumbers.failure }}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="success--text">Success: {{ values.success }}</div>
|
||||||
|
<div class="error--text">Failed: {{ values.failure }}</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</div>
|
</div>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
@ -131,26 +101,35 @@ export default {
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
recipeNumbers() {
|
recipeNumbers() {
|
||||||
return this.calculateNumbers(this.recipeData);
|
return this.calculateNumbers("Recipes", this.recipeData);
|
||||||
},
|
},
|
||||||
settingsNumbers() {
|
settingsNumbers() {
|
||||||
return this.calculateNumbers(this.settingsData);
|
return this.calculateNumbers("Settings", this.settingsData);
|
||||||
},
|
},
|
||||||
themeNumbers() {
|
themeNumbers() {
|
||||||
return this.calculateNumbers(this.themeData);
|
return this.calculateNumbers("Theme", this.themeData);
|
||||||
},
|
},
|
||||||
userNumbers() {
|
userNumbers() {
|
||||||
return this.calculateNumbers(this.userData);
|
return this.calculateNumbers("Users", this.userData);
|
||||||
},
|
},
|
||||||
groupNumbers() {
|
groupNumbers() {
|
||||||
return this.calculateNumbers(this.groupData);
|
return this.calculateNumbers("Groups", this.groupData);
|
||||||
|
},
|
||||||
|
allNumbers() {
|
||||||
|
return [
|
||||||
|
this.recipeNumbers,
|
||||||
|
this.settingsNumbers,
|
||||||
|
this.themeNumbers,
|
||||||
|
this.userNumbers,
|
||||||
|
this.groupNumbers,
|
||||||
|
];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
calculateNumbers(list_array) {
|
calculateNumbers(title, list_array) {
|
||||||
if (!list_array) return;
|
if (!list_array) return;
|
||||||
let numbers = { success: 0, failure: 0 };
|
let numbers = { title: title, success: 0, failure: 0 };
|
||||||
list_array.forEach(element => {
|
list_array.forEach(element => {
|
||||||
if (element.status) {
|
if (element.status) {
|
||||||
numbers.success++;
|
numbers.success++;
|
||||||
|
|
11
makefile
11
makefile
|
@ -4,10 +4,9 @@ setup:
|
||||||
npm install && \
|
npm install && \
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
source ./.venv/bin/activate && \
|
poetry run python mealie/db/init_db.py && \
|
||||||
python mealie/db/init_db.py && \
|
poetry run python mealie/app.py
|
||||||
python mealie/app.py
|
|
||||||
|
|
||||||
.PHONY: frontend
|
.PHONY: frontend
|
||||||
frontend:
|
frontend:
|
||||||
|
@ -15,9 +14,7 @@ frontend:
|
||||||
|
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
docs:
|
docs:
|
||||||
source ./.venv/bin/activate && \
|
cd docs && poetry run python -m mkdocs serve
|
||||||
cd docs && \
|
|
||||||
mkdocs serve
|
|
||||||
|
|
||||||
docker-dev:
|
docker-dev:
|
||||||
docker-compose -f docker-compose.dev.yml -p dev-mealie up --build
|
docker-compose -f docker-compose.dev.yml -p dev-mealie up --build
|
||||||
|
|
|
@ -76,7 +76,7 @@ class BaseDocument:
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get(self, session: Session, match_value: str, match_key: str = None, limit=1) -> dict or List[dict]:
|
def get(self, session: Session, match_value: str, match_key: str = None, limit=1) -> BaseModel or List[BaseModel]:
|
||||||
"""Retrieves an entry from the database by matching a key/value pair. If no
|
"""Retrieves an entry from the database by matching a key/value pair. If no
|
||||||
key is provided the class objects primary key will be used to match against.
|
key is provided the class objects primary key will be used to match against.
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class BaseDocument:
|
||||||
return None
|
return None
|
||||||
return [self.schema.from_orm(x) for x in result]
|
return [self.schema.from_orm(x) for x in result]
|
||||||
|
|
||||||
def create(self, session: Session, document: dict) -> dict:
|
def create(self, session: Session, document: dict) -> BaseModel:
|
||||||
"""Creates a new database entry for the given SQL Alchemy Model.
|
"""Creates a new database entry for the given SQL Alchemy Model.
|
||||||
|
|
||||||
Args: \n
|
Args: \n
|
||||||
|
@ -121,7 +121,7 @@ class BaseDocument:
|
||||||
return_data = new_document.dict()
|
return_data = new_document.dict()
|
||||||
return return_data
|
return return_data
|
||||||
|
|
||||||
def update(self, session: Session, match_value: str, new_data: str) -> dict:
|
def update(self, session: Session, match_value: str, new_data: str) -> BaseModel:
|
||||||
"""Update a database entry.
|
"""Update a database entry.
|
||||||
|
|
||||||
Args: \n
|
Args: \n
|
||||||
|
|
|
@ -7,7 +7,6 @@ from typing import List
|
||||||
from fastapi.logger import logger
|
from fastapi.logger import logger
|
||||||
from mealie.core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
|
from mealie.core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import create_session
|
|
||||||
from mealie.schema.recipe import Recipe
|
from mealie.schema.recipe import Recipe
|
||||||
from mealie.schema.restore import GroupImport, RecipeImport, SettingsImport, ThemeImport, UserImport
|
from mealie.schema.restore import GroupImport, RecipeImport, SettingsImport, ThemeImport, UserImport
|
||||||
from mealie.schema.theme import SiteTheme
|
from mealie.schema.theme import SiteTheme
|
||||||
|
@ -47,38 +46,41 @@ class ImportDatabase:
|
||||||
raise Exception("Import file does not exist")
|
raise Exception("Import file does not exist")
|
||||||
|
|
||||||
def import_recipes(self):
|
def import_recipes(self):
|
||||||
session = create_session()
|
|
||||||
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
||||||
|
|
||||||
imports = []
|
imports = []
|
||||||
successful_imports = []
|
successful_imports = []
|
||||||
|
|
||||||
for recipe in recipe_dir.glob("*.json"):
|
def read_recipe_file(file_path: Path):
|
||||||
with open(recipe, "r") as f:
|
with open(file_path, "r") as f:
|
||||||
recipe_dict = json.loads(f.read())
|
try:
|
||||||
recipe_dict = ImportDatabase._recipe_migration(recipe_dict)
|
recipe_dict = json.loads(f.read())
|
||||||
try:
|
recipe_dict = ImportDatabase._recipe_migration(recipe_dict)
|
||||||
if recipe_dict.get("categories", False):
|
return Recipe(**recipe_dict)
|
||||||
recipe_dict["recipeCategory"] = recipe_dict.get("categories")
|
except:
|
||||||
del recipe_dict["categories"]
|
import_status = RecipeImport(name=file_path.stem, slug=file_path.stem, status=False)
|
||||||
|
imports.append(import_status)
|
||||||
|
|
||||||
recipe_obj = Recipe(**recipe_dict)
|
recipes = [read_recipe_file(r) for r in recipe_dir.glob("*.json")]
|
||||||
db.recipes.create(session, recipe_obj.dict())
|
|
||||||
import_status = RecipeImport(name=recipe_obj.name, slug=recipe_obj.slug, status=True)
|
for recipe in recipes:
|
||||||
imports.append(import_status)
|
try:
|
||||||
successful_imports.append(recipe.stem)
|
db.recipes.create(self.session, recipe.dict())
|
||||||
logger.info(f"Imported: {recipe.stem}")
|
import_status = RecipeImport(name=recipe.name, slug=recipe.slug, status=True)
|
||||||
|
successful_imports.append(recipe.slug)
|
||||||
|
logger.info(f"Imported: {recipe.slug}")
|
||||||
|
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
self.session.rollback()
|
||||||
logger.error(inst)
|
logger.error(inst)
|
||||||
logger.info(f"Failed Import: {recipe.stem}")
|
logger.info(f"Failed Import: {recipe.slug}")
|
||||||
import_status = RecipeImport(
|
import_status = RecipeImport(
|
||||||
name=recipe.stem,
|
name=recipe.name,
|
||||||
slug=recipe.stem,
|
slug=recipe.slug,
|
||||||
status=False,
|
status=False,
|
||||||
exception=str(inst),
|
exception=str(inst),
|
||||||
)
|
)
|
||||||
imports.append(import_status)
|
|
||||||
|
imports.append(import_status)
|
||||||
|
|
||||||
self._import_images(successful_imports)
|
self._import_images(successful_imports)
|
||||||
|
|
||||||
|
@ -86,6 +88,9 @@ class ImportDatabase:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _recipe_migration(recipe_dict: dict) -> dict:
|
def _recipe_migration(recipe_dict: dict) -> dict:
|
||||||
|
if recipe_dict.get("categories", False):
|
||||||
|
recipe_dict["recipeCategory"] = recipe_dict.get("categories")
|
||||||
|
del recipe_dict["categories"]
|
||||||
try:
|
try:
|
||||||
del recipe_dict["_id"]
|
del recipe_dict["_id"]
|
||||||
del recipe_dict["dateAdded"]
|
del recipe_dict["dateAdded"]
|
||||||
|
@ -117,6 +122,9 @@ class ImportDatabase:
|
||||||
|
|
||||||
def import_themes(self):
|
def import_themes(self):
|
||||||
themes_file = self.import_dir.joinpath("themes", "themes.json")
|
themes_file = self.import_dir.joinpath("themes", "themes.json")
|
||||||
|
if not themes_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
theme_imports = []
|
theme_imports = []
|
||||||
with open(themes_file, "r") as f:
|
with open(themes_file, "r") as f:
|
||||||
themes: list[dict] = json.loads(f.read())
|
themes: list[dict] = json.loads(f.read())
|
||||||
|
@ -141,6 +149,9 @@ class ImportDatabase:
|
||||||
|
|
||||||
def import_settings(self):
|
def import_settings(self):
|
||||||
settings_file = self.import_dir.joinpath("settings", "settings.json")
|
settings_file = self.import_dir.joinpath("settings", "settings.json")
|
||||||
|
if not settings_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
settings_imports = []
|
settings_imports = []
|
||||||
|
|
||||||
with open(settings_file, "r") as f:
|
with open(settings_file, "r") as f:
|
||||||
|
@ -153,6 +164,7 @@ class ImportDatabase:
|
||||||
import_status = SettingsImport(name=name, status=True)
|
import_status = SettingsImport(name=name, status=True)
|
||||||
|
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
self.session.rollback()
|
||||||
import_status = SettingsImport(name=name, status=False, exception=str(inst))
|
import_status = SettingsImport(name=name, status=False, exception=str(inst))
|
||||||
|
|
||||||
settings_imports.append(import_status)
|
settings_imports.append(import_status)
|
||||||
|
@ -160,6 +172,9 @@ class ImportDatabase:
|
||||||
|
|
||||||
def import_groups(self):
|
def import_groups(self):
|
||||||
groups_file = self.import_dir.joinpath("groups", "groups.json")
|
groups_file = self.import_dir.joinpath("groups", "groups.json")
|
||||||
|
if not groups_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
group_imports = []
|
group_imports = []
|
||||||
|
|
||||||
with open(groups_file, "r") as f:
|
with open(groups_file, "r") as f:
|
||||||
|
@ -176,6 +191,7 @@ class ImportDatabase:
|
||||||
import_status = GroupImport(name=group.name, status=True)
|
import_status = GroupImport(name=group.name, status=True)
|
||||||
|
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
self.session.rollback()
|
||||||
import_status = GroupImport(name=group.name, status=False, exception=str(inst))
|
import_status = GroupImport(name=group.name, status=False, exception=str(inst))
|
||||||
|
|
||||||
group_imports.append(import_status)
|
group_imports.append(import_status)
|
||||||
|
@ -184,6 +200,9 @@ class ImportDatabase:
|
||||||
|
|
||||||
def import_users(self):
|
def import_users(self):
|
||||||
users_file = self.import_dir.joinpath("users", "users.json")
|
users_file = self.import_dir.joinpath("users", "users.json")
|
||||||
|
if not users_file.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
user_imports = []
|
user_imports = []
|
||||||
|
|
||||||
with open(users_file, "r") as f:
|
with open(users_file, "r") as f:
|
||||||
|
@ -207,6 +226,7 @@ class ImportDatabase:
|
||||||
import_status = UserImport(name=user.full_name, status=True)
|
import_status = UserImport(name=user.full_name, status=True)
|
||||||
|
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
self.session.rollback()
|
||||||
import_status = UserImport(name=user.full_name, status=False, exception=str(inst))
|
import_status = UserImport(name=user.full_name, status=False, exception=str(inst))
|
||||||
|
|
||||||
user_imports.append(import_status)
|
user_imports.append(import_status)
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from mealie.core.config import TEMP_DIR
|
|
||||||
from mealie.schema.recipe import Recipe
|
|
||||||
from mealie.services.image_services import IMG_DIR
|
|
||||||
from mealie.services.migrations.nextcloud import (
|
|
||||||
cleanup,
|
|
||||||
import_recipes,
|
|
||||||
prep,
|
|
||||||
process_selection,
|
|
||||||
)
|
|
||||||
from tests.test_config import TEST_NEXTCLOUD_DIR
|
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
|
||||||
TEST_NEXTCLOUD_DIR
|
|
||||||
TEMP_NEXTCLOUD = TEMP_DIR.joinpath("nextcloud")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"file_name,final_path",
|
|
||||||
[("nextcloud.zip", TEMP_NEXTCLOUD)],
|
|
||||||
)
|
|
||||||
def test_zip_extraction(file_name: str, final_path: Path):
|
|
||||||
prep()
|
|
||||||
zip = TEST_NEXTCLOUD_DIR.joinpath(file_name)
|
|
||||||
dir = process_selection(zip)
|
|
||||||
|
|
||||||
assert dir == final_path
|
|
||||||
cleanup()
|
|
||||||
assert dir.exists() == False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"recipe_dir",
|
|
||||||
[
|
|
||||||
TEST_NEXTCLOUD_DIR.joinpath("Air Fryer Shrimp"),
|
|
||||||
TEST_NEXTCLOUD_DIR.joinpath("Chicken Parmigiana"),
|
|
||||||
TEST_NEXTCLOUD_DIR.joinpath("Skillet Shepherd's Pie"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_nextcloud_migration(recipe_dir: Path):
|
|
||||||
recipe = import_recipes(recipe_dir)
|
|
||||||
assert isinstance(recipe, Recipe)
|
|
||||||
IMG_DIR.joinpath(recipe.image).unlink(missing_ok=True)
|
|
Loading…
Add table
Add a link
Reference in a new issue