potentially fix #217

This commit is contained in:
hay-kot 2021-03-21 12:19:03 -08:00
commit 802633a060
6 changed files with 68 additions and 117 deletions

View file

@ -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++;

View file

@ -5,9 +5,8 @@ setup:
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

View file

@ -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

View file

@ -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)

View file

@ -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)