From 894d6b9c9b57fc27357f071c2c280e7c302c1e4f Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sat, 27 Mar 2021 13:23:08 -0800 Subject: [PATCH] Feature/submit on enter key (#224) * general cleanup * submit on enter * fix signup form * fix duplicate slugs when testing Co-authored-by: hay-kot --- .../Admin/ManageUsers/GroupDashboard.vue | 31 +++++---- .../Admin/ManageUsers/TheSignUpTable.vue | 55 ++++++++-------- .../Admin/ManageUsers/TheUserTable.vue | 65 +++++++++++-------- .../components/Admin/Theme/NewThemeDialog.vue | 36 +++++----- frontend/src/components/Login/LoginForm.vue | 41 ++++++------ frontend/src/components/Login/SignUpForm.vue | 32 ++++----- frontend/src/components/UI/AddRecipeFab.vue | 45 +++++++------ mealie/db/models/model_base.py | 1 - mealie/routes/deps.py | 1 - mealie/routes/mealplans/crud.py | 1 - mealie/routes/users/crud.py | 9 +-- mealie/schema/backup.py | 8 +-- mealie/services/backups/imports.py | 18 ++--- mealie/services/meal_services.py | 14 +--- mealie/services/migrations/chowdown.py | 24 ++----- mealie/services/scraper/cleaner.py | 8 +-- mealie/services/scraper/scraper.py | 5 +- tests/conftest.py | 4 -- tests/test_routes/test_import_routes.py | 25 +++++++ tests/test_routes/test_migration_routes.py | 1 + tests/test_routes/test_recipe_routes.py | 1 + tests/test_routes/test_user_routes.py | 1 - tests/utils.py | 0 23 files changed, 216 insertions(+), 210 deletions(-) create mode 100644 tests/test_routes/test_import_routes.py delete mode 100644 tests/utils.py diff --git a/frontend/src/components/Admin/ManageUsers/GroupDashboard.vue b/frontend/src/components/Admin/ManageUsers/GroupDashboard.vue index 15a22b96f..4407c750a 100644 --- a/frontend/src/components/Admin/ManageUsers/GroupDashboard.vue +++ b/frontend/src/components/Admin/ManageUsers/GroupDashboard.vue @@ -24,7 +24,7 @@ v-bind="attrs" v-on="on" > - {{ $t('user.create-group') }} + {{ $t("user.create-group") }} @@ -34,31 +34,30 @@ - {{ $t('user.create-group') }} + {{ $t("user.create-group") }} - - - + + - - + - - - - {{ $t('general.cancel') }} - - - {{ $t('general.create') }} - - + + + + {{ $t("general.cancel") }} + + + {{ $t("general.create") }} + + + diff --git a/frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue b/frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue index 42acc597f..b44368cf7 100644 --- a/frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue +++ b/frontend/src/components/Admin/ManageUsers/TheSignUpTable.vue @@ -3,7 +3,11 @@ - {{ $t('user.sign-up-links') }} + {{ $t("user.sign-up-links") }} @@ -31,14 +35,13 @@ - {{ $t('user.create-link') }} + {{ $t("user.create-link") }} - - - + + - - + - - - - {{ $t('general.cancel') }} - - - {{ $t('general.save') }} - - + + + + {{ $t("general.cancel") }} + + + {{ $t("general.save") }} + + + @@ -90,7 +93,7 @@ mdi-account-cog - {{ item.admin ? $t('general.yes') : $t('general.no') }} + {{ item.admin ? $t("general.yes") : $t("general.no") }} @@ -113,21 +116,21 @@ import { validators } from "@/mixins/validators"; export default { components: { Confirmation }, mixins: [validators], - data() { + data() { return { dialog: false, activeId: null, activeName: null, headers: [ { - text: this.$t('user.link-id'), + text: this.$t("user.link-id"), align: "start", sortable: false, value: "id", }, - { text: this.$t('general.name'), value: "name" }, - { text: this.$t('general.token'), value: "token" }, - { text: this.$t('user.admin'), value: "admin", align: "center" }, + { text: this.$t("general.name"), value: "name" }, + { text: this.$t("general.token"), value: "token" }, + { text: this.$t("user.admin"), value: "admin", align: "center" }, { text: "", value: "actions", sortable: false, align: "center" }, ], links: [], @@ -144,7 +147,7 @@ export default { admin: false, id: 0, }, - } + }; }, computed: { diff --git a/frontend/src/components/Admin/ManageUsers/TheUserTable.vue b/frontend/src/components/Admin/ManageUsers/TheUserTable.vue index 0c268c813..ae51ba232 100644 --- a/frontend/src/components/Admin/ManageUsers/TheUserTable.vue +++ b/frontend/src/components/Admin/ManageUsers/TheUserTable.vue @@ -3,7 +3,12 @@ @@ -40,12 +45,11 @@ - {{$t('user.user-id-with-value', {id: editedItem.id }) }} + {{ $t("user.user-id-with-value", { id: editedItem.id }) }} - - - + + - + - - + - - - - {{$t('general.cancel')}} - - - {{$t('general.save')}} - - + + + + {{ $t("general.cancel") }} + + + {{ $t("general.save") }} + + + @@ -111,13 +118,13 @@ mdi-delete - {{$t('general.delete')}} + {{ $t("general.delete") }} mdi-pencil - {{$t('general.edit')}} + {{ $t("general.edit") }} @@ -140,7 +147,7 @@ import { validators } from "@/mixins/validators"; export default { components: { Confirmation }, mixins: [validators], - data() { + data() { return { search: "", dialog: false, @@ -153,10 +160,10 @@ export default { sortable: false, value: "id", }, - { text: this.$t('user.full-name'), value: "fullName" }, - { text: this.$t('user.email'), value: "email" }, - { text: this.$t('user.group'), value: "group" }, - { text: this.$t('user.admin'), value: "admin" }, + { text: this.$t("user.full-name"), value: "fullName" }, + { text: this.$t("user.email"), value: "email" }, + { text: this.$t("user.group"), value: "group" }, + { text: this.$t("user.admin"), value: "admin" }, { text: "", value: "actions", sortable: false, align: "center" }, ], users: [], @@ -177,12 +184,14 @@ export default { group: "", admin: false, }, - } + }; }, computed: { formTitle() { - return this.editedIndex === -1 ? this.$t('user.new-user') : this.$t('user.edit-user'); + return this.editedIndex === -1 + ? this.$t("user.new-user") + : this.$t("user.edit-user"); }, showPassword() { return this.editedIndex === -1 ? true : false; diff --git a/frontend/src/components/Admin/Theme/NewThemeDialog.vue b/frontend/src/components/Admin/Theme/NewThemeDialog.vue index af072170b..aff8b0bb2 100644 --- a/frontend/src/components/Admin/Theme/NewThemeDialog.vue +++ b/frontend/src/components/Admin/Theme/NewThemeDialog.vue @@ -17,22 +17,24 @@ - - - - - - - {{ $t("general.cancel") }} - - - {{ $t("general.create") }} - - + + + + + + + + {{ $t("general.cancel") }} + + + {{ $t("general.create") }} + + + @@ -64,7 +66,7 @@ export default { randomColor() { return "#" + Math.floor(Math.random() * 16777215).toString(16); }, - Select() { + select() { const newTheme = { name: this.themeName, colors: { diff --git a/frontend/src/components/Login/LoginForm.vue b/frontend/src/components/Login/LoginForm.vue index daa5700b2..108219f96 100644 --- a/frontend/src/components/Login/LoginForm.vue +++ b/frontend/src/components/Login/LoginForm.vue @@ -13,11 +13,12 @@ class="mr-2" > - {{$t('user.login')}} + {{ $t("user.login") }} - - + + + - - - {{ $t("user.sign-in") }} - - - {{$t('user.could-not-validate-credentials')}} - - + + {{ $t("user.sign-in") }} + + + + + {{ $t("user.could-not-validate-credentials") }} + + + diff --git a/frontend/src/components/Login/SignUpForm.vue b/frontend/src/components/Login/SignUpForm.vue index 52baab74a..a7fc0e962 100644 --- a/frontend/src/components/Login/SignUpForm.vue +++ b/frontend/src/components/Login/SignUpForm.vue @@ -21,7 +21,7 @@ have a valid invitation link. If you haven't recieved an invitation you are unable to sign-up. To recieve a link, contact the sites administrator. - + + + + Sign Up + + + + Error Signing Up + - - - Sign Up - - - - Error Signing Up - diff --git a/frontend/src/components/UI/AddRecipeFab.vue b/frontend/src/components/UI/AddRecipeFab.vue index c3f9c3e0f..506e000b3 100644 --- a/frontend/src/components/UI/AddRecipeFab.vue +++ b/frontend/src/components/UI/AddRecipeFab.vue @@ -21,9 +21,8 @@ - - - + + - - - {{ $t("new-recipe.error-message") }} - - + + {{ $t("new-recipe.error-message") }} + + - + - - - - {{ $t("general.close") }} - - - {{ $t("general.submit") }} - - + + + + {{ $t("general.close") }} + + + {{ $t("general.submit") }} + + + diff --git a/mealie/db/models/model_base.py b/mealie/db/models/model_base.py index f62ec6cdf..44589cfe8 100644 --- a/mealie/db/models/model_base.py +++ b/mealie/db/models/model_base.py @@ -42,7 +42,6 @@ def update_generics(func): elif attribute: setattr(class_object, key, value) - print("Complex", complex_attributed) func(class_object, complex_attributed) return wrapper diff --git a/mealie/routes/deps.py b/mealie/routes/deps.py index acf9f8e94..7cba04e3c 100644 --- a/mealie/routes/deps.py +++ b/mealie/routes/deps.py @@ -23,7 +23,6 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends( if username is None: raise credentials_exception token_data = TokenData(username=username) - print("Login Payload", token_data) except JWTError: raise credentials_exception user = db.users.get(session, token_data.username, "email") diff --git a/mealie/routes/mealplans/crud.py b/mealie/routes/mealplans/crud.py index 1c6da143f..906d0029d 100644 --- a/mealie/routes/mealplans/crud.py +++ b/mealie/routes/mealplans/crud.py @@ -77,6 +77,5 @@ def get_today( group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name") recipe = get_todays_meal(session, group_in_db) - print(datetime.date.today()) return recipe.slug diff --git a/mealie/routes/users/crud.py b/mealie/routes/users/crud.py index c18b06489..af16df950 100644 --- a/mealie/routes/users/crud.py +++ b/mealie/routes/users/crud.py @@ -68,14 +68,11 @@ async def update_user( token = None if current_user.id == id or current_user.admin: - print("Current User") db.users.update(session, id, new_data.dict()) - if current_user.id == id: - print(new_data.email) - access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2)) - token = {"access_token": access_token, "token_type": "bearer"} + if current_user.id == id: + access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2)) + token = {"access_token": access_token, "token_type": "bearer"} - print(SnackResponse.success("User Updated", token)) return SnackResponse.success("User Updated", token) diff --git a/mealie/schema/backup.py b/mealie/schema/backup.py index 908d2df37..12c0f3b3e 100644 --- a/mealie/schema/backup.py +++ b/mealie/schema/backup.py @@ -33,10 +33,10 @@ class ImportJob(BackupOptions): "example": { "name": "my_local_backup.zip", "recipes": True, - "force": False, - "rebase": False, - "themes": False, - "settings": False, + "settings": True, + "themes": True, + "groups": True, + "users": True, } } diff --git a/mealie/services/backups/imports.py b/mealie/services/backups/imports.py index 0f5baeab2..ca99e8480 100644 --- a/mealie/services/backups/imports.py +++ b/mealie/services/backups/imports.py @@ -89,13 +89,16 @@ class ImportDatabase: # Migration from list to Object Type Data try: if "" in recipe_dict["tags"]: - recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if not tag == ""] + recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""] except: pass try: if "" in recipe_dict["categories"]: - recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if not cat == ""] + recipe_dict["categories"] = [ + cat for cat in recipe_dict["categories"] if cat != "" + ] + except: pass @@ -239,16 +242,15 @@ class ImportDatabase: item = db_table.get(self.session, search_value, search_key) if item: - if self.force_imports: - primary_key = getattr(item, db_table.primary_key) - db_table.delete(self.session, primary_key) - else: + if not self.force_imports: return return_model( name=model_name, status=False, exception=f"Table entry with matching '{search_key}': '{search_value}' exists", ) + primary_key = getattr(item, db_table.primary_key) + db_table.delete(self.session, primary_key) try: db_table.create(self.session, model.dict()) import_status = return_model(name=model_name, status=True) @@ -301,12 +303,10 @@ def import_database( import_session.clean_up() - data = { + return { "recipeImports": recipe_report, "settingsImports": settings_report, "themeImports": theme_report, "groupImports": group_report, "userImports": user_report, } - - return data diff --git a/mealie/services/meal_services.py b/mealie/services/meal_services.py index 023daea37..a7d3150f2 100644 --- a/mealie/services/meal_services.py +++ b/mealie/services/meal_services.py @@ -4,26 +4,16 @@ from typing import Union import pytz from mealie.db.database import db from mealie.db.db_setup import create_session -from pydantic.tools import T -from mealie.schema.meal import ( - MealIn, - MealOut, - MealPlanIn, - MealPlanInDB, - MealPlanProcessed, -) +from mealie.schema.meal import MealIn, MealOut, MealPlanIn, MealPlanInDB, MealPlanProcessed from mealie.schema.recipe import Recipe from mealie.schema.user import GroupInDB +from pydantic.tools import T from sqlalchemy.orm.session import Session def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed: meals = [] for x, meal in enumerate(meal_plan_base.meals): - # europe = pytz.timezone("America/Anchorage") - # d = europe.localize(meal_plan_base.startDate) - # print(d) - meal: MealIn try: recipe: Recipe = db.recipes.get(session, meal.slug) diff --git a/mealie/services/migrations/chowdown.py b/mealie/services/migrations/chowdown.py index ab088d9bb..fe9725a96 100644 --- a/mealie/services/migrations/chowdown.py +++ b/mealie/services/migrations/chowdown.py @@ -2,11 +2,12 @@ import shutil from pathlib import Path import yaml +from fastapi.logger import logger from mealie.core.config import IMG_DIR, TEMP_DIR from mealie.db.database import db from mealie.schema.recipe import Recipe -from sqlalchemy.orm.session import Session from mealie.utils.unzip import unpack_zip +from sqlalchemy.orm.session import Session try: from yaml import CLoader as Loader @@ -50,11 +51,8 @@ def read_chowdown_file(recipe_file: Path) -> Recipe: "tags": recipe_data.get("tags").split(","), } - print(reformat_data) + reformated_list = [{"text": instruction} for instruction in reformat_data["recipeInstructions"]] - reformated_list = [] - for instruction in reformat_data["recipeInstructions"]: - reformated_list.append({"text": instruction}) reformat_data["recipeInstructions"] = reformated_list return Recipe(**reformat_data) @@ -63,21 +61,12 @@ def read_chowdown_file(recipe_file: Path) -> Recipe: def chowdown_migrate(session: Session, zip_file: Path): temp_dir = unpack_zip(zip_file) - print(temp_dir.name) - path = Path(temp_dir.name) - for p in path.iterdir(): - print("ItterDir", p) - for p in p.iterdir(): - print("Sub Itter", p) with temp_dir as dir: chow_dir = next(Path(dir).iterdir()) image_dir = TEMP_DIR.joinpath(chow_dir, "images") recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes") - print(image_dir.exists()) - print(recipe_dir.exists()) - failed_recipes = [] successful_recipes = [] for recipe in recipe_dir.glob("*.md"): @@ -86,17 +75,18 @@ def chowdown_migrate(session: Session, zip_file: Path): db.recipes.create(session, new_recipe.dict()) successful_recipes.append(new_recipe.name) except Exception as inst: + session.rollback() + logger.error(inst) failed_recipes.append(recipe.stem) failed_images = [] for image in image_dir.iterdir(): try: - if not image.stem in failed_recipes: + if image.stem not in failed_recipes: shutil.copy(image, IMG_DIR.joinpath(image.name)) except Exception as inst: - print(inst) + logger.error(inst) failed_images.append(image.name) - report = {"successful": successful_recipes, "failed": failed_recipes} return report diff --git a/mealie/services/scraper/cleaner.py b/mealie/services/scraper/cleaner.py index 7f680ff6a..a6811114d 100644 --- a/mealie/services/scraper/cleaner.py +++ b/mealie/services/scraper/cleaner.py @@ -52,8 +52,7 @@ class Cleaner: @staticmethod def html(raw_html): cleanr = re.compile("<.*?>") - cleantext = re.sub(cleanr, "", raw_html) - return cleantext + return re.sub(cleanr, "", raw_html) @staticmethod def image(image=None) -> str: @@ -142,12 +141,11 @@ class Cleaner: @staticmethod def time(time_entry): - print(time_entry, type(time_entry)) - if time_entry == None: + if time_entry is None: return None elif type(time_entry) == datetime: print(time_entry) elif type(time_entry) != str: return str(time_entry) - elif time_entry != None: + else: return time_entry diff --git a/mealie/services/scraper/scraper.py b/mealie/services/scraper/scraper.py index 48def344f..ff2139a93 100644 --- a/mealie/services/scraper/scraper.py +++ b/mealie/services/scraper/scraper.py @@ -25,13 +25,10 @@ def create_from_url(url: str) -> Recipe: """ r = requests.get(url) new_recipe = extract_recipe_from_html(r.text, url) - print(new_recipe) new_recipe = Cleaner.clean(new_recipe, url) new_recipe = download_image_for_recipe(new_recipe) - recipe = Recipe(**new_recipe) - - return recipe + return Recipe(**new_recipe) def extract_recipe_from_html(html: str, url: str) -> dict: diff --git a/tests/conftest.py b/tests/conftest.py index d3f202887..0dfd75af0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,13 +4,9 @@ import requests from fastapi.testclient import TestClient from mealie.app import app from mealie.core.config import SQLITE_DIR -from mealie.db.database import db from mealie.db.db_setup import generate_session, sql_global_init from mealie.db.init_db import init_db -from mealie.routes.deps import get_current_user -from mealie.schema.user import UserInDB from pytest import fixture -from sqlalchemy.orm.session import Session from tests.test_config import TEST_DATA diff --git a/tests/test_routes/test_import_routes.py b/tests/test_routes/test_import_routes.py new file mode 100644 index 000000000..0dfc82d1d --- /dev/null +++ b/tests/test_routes/test_import_routes.py @@ -0,0 +1,25 @@ +import json + +import pytest + + +@pytest.fixture +def backup_data(): + return { + "name": "dev_sample_data_2021-Feb-13.zip", + "force": False, + "recipes": True, + "settings": False, #! Broken + "themes": True, + "groups": True, + "users": True, + } + + +def test_import(api_client, backup_data): + response = api_client.post("/api/backups/dev_sample_data_2021-Feb-13.zip/import", json=backup_data) + + assert response.status_code == 200 + for key, value in json.loads(response.content).items(): + for v in value: + assert v["status"] == True diff --git a/tests/test_routes/test_migration_routes.py b/tests/test_routes/test_migration_routes.py index e734f6d4a..f0629bf1b 100644 --- a/tests/test_routes/test_migration_routes.py +++ b/tests/test_routes/test_migration_routes.py @@ -34,6 +34,7 @@ def test_upload_chowdown_zip(api_client, chowdown_zip): def test_import_chowdown_directory(api_client, chowdown_zip): + api_client.delete(f"{RECIPES_PREFIX}/roasted-okra") # TODO: Manage Test Data better selection = chowdown_zip.name response = api_client.post(f"{MIGRATIONS_PREFIX}/chowdown/{selection}/import") diff --git a/tests/test_routes/test_recipe_routes.py b/tests/test_routes/test_recipe_routes.py index 2a0007537..1007b2d09 100644 --- a/tests/test_routes/test_recipe_routes.py +++ b/tests/test_routes/test_recipe_routes.py @@ -14,6 +14,7 @@ def test_create_by_url(api_client, recipe_data: RecipeTestData): def test_create_by_json(api_client): + api_client.delete(f"{RECIPES_PREFIX}/banana-bread") response = api_client.post(RECIPES_CREATE, json=raw_recipe) assert response.status_code == 201 diff --git a/tests/test_routes/test_user_routes.py b/tests/test_routes/test_user_routes.py index db3ba1c03..4d3aef113 100644 --- a/tests/test_routes/test_user_routes.py +++ b/tests/test_routes/test_user_routes.py @@ -66,7 +66,6 @@ def test_update_user(api_client: requests, token): response = api_client.put(f"{BASE}/1", headers=token, json=update_data) assert response.status_code == 200 - print(response.text) assert json.loads(response.text).get("access_token") diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index e69de29bb..000000000