mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
Refactor/recipe routes (#370)
* format with black * black format * flake8 * remove bar exceptions * remove test for depreciated route * recipe settings editr * add sqlite Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
5dafe8fbb5
commit
1dc051f562
2
.flake8
2
.flake8
@ -1,6 +1,6 @@
|
|||||||
[flake8]
|
[flake8]
|
||||||
ignore = [
|
ignore = [
|
||||||
E501 # Line Length - See Black Config in pyproject.toml
|
E501 # Line Length - See Black Config in pyproject.toml
|
||||||
E722 # Bare Exception | Temporary
|
E402 # Import Not at Top of File
|
||||||
]
|
]
|
||||||
exclude = _all_models.py
|
exclude = _all_models.py
|
||||||
|
23
.gitignore
vendored
23
.gitignore
vendored
@ -3,13 +3,11 @@
|
|||||||
*__pycache__/
|
*__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
# frontend/.env.development
|
# frontend/.env.development
|
||||||
docs/site/
|
docs/site/
|
||||||
mealie/temp/*
|
*temp*
|
||||||
mealie/temp/api.html
|
|
||||||
.temp/
|
|
||||||
.secret
|
.secret
|
||||||
!*/components/Recipe/Parts
|
|
||||||
|
|
||||||
dev/data/backups/*
|
dev/data/backups/*
|
||||||
dev/data/debug/*
|
dev/data/debug/*
|
||||||
@ -17,14 +15,6 @@ dev/data/img/*
|
|||||||
dev/data/migration/*
|
dev/data/migration/*
|
||||||
dev/data/users/*
|
dev/data/users/*
|
||||||
|
|
||||||
#Exception to keep folders
|
|
||||||
!mealie/dist/.gitkeep
|
|
||||||
!dev/data/backups/.gitkeep
|
|
||||||
!dev/data/backups/dev_sample_data*
|
|
||||||
!dev/data/debug/.gitkeep
|
|
||||||
!dev/data/migration/.gitkeep
|
|
||||||
!dev/data/img/.gitkeep
|
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
@ -148,16 +138,11 @@ ENV/
|
|||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
# IDE settings
|
|
||||||
# .vscode/
|
|
||||||
|
|
||||||
# Node Modules
|
# Node Modules
|
||||||
node_modules/
|
node_modules/
|
||||||
mealie/data/debug/last_recipe.json
|
*.db
|
||||||
*.sqlite
|
*.sqlite
|
||||||
dev/data/db/test.db
|
|
||||||
scratch.py
|
scratch.py
|
||||||
dev/data/backups/dev_sample_data*.zip
|
dev/data/backups/dev_sample_data*.zip
|
||||||
dev/data/backups/dev_sample_data*.zip
|
|
||||||
!dev/data/backups/test*.zip
|
!dev/data/backups/test*.zip
|
||||||
dev/data/recipes/*
|
dev/data/recipes/*
|
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="text-center">
|
||||||
|
<v-menu offset-y top nudge-top="6" :close-on-content-click="false">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn color="accent" dark v-bind="attrs" v-on="on">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-cog
|
||||||
|
</v-icon>
|
||||||
|
{{ $t("general.settings") }}
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="py-2">
|
||||||
|
<div>
|
||||||
|
Recipe Settings
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
<v-divider class="mx-2"></v-divider>
|
||||||
|
<v-card-text class="mt-n5">
|
||||||
|
<v-switch
|
||||||
|
dense
|
||||||
|
v-for="(itemValue, key) in value"
|
||||||
|
:key="key"
|
||||||
|
v-model="value[key]"
|
||||||
|
flat
|
||||||
|
inset
|
||||||
|
:label="labels[key]"
|
||||||
|
hide-details
|
||||||
|
></v-switch>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
value: Object,
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
labels: {
|
||||||
|
public: "Public Recipe",
|
||||||
|
showNutrition: "Show Nutrition Values",
|
||||||
|
showAssets: "Show Assets",
|
||||||
|
landscapeView: "Landscape View (Coming Soon)",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
methods: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
@ -8,6 +8,11 @@
|
|||||||
:slug="value.slug"
|
:slug="value.slug"
|
||||||
@refresh="$emit('upload')"
|
@refresh="$emit('upload')"
|
||||||
/>
|
/>
|
||||||
|
<SettingsMenu
|
||||||
|
class="my-1 mx-1"
|
||||||
|
@upload="uploadImage"
|
||||||
|
:value="value.settings"
|
||||||
|
/>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<v-col>
|
<v-col>
|
||||||
@ -122,6 +127,7 @@ import Instructions from "@/components/Recipe/Parts/Instructions";
|
|||||||
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
||||||
import Assets from "@/components/Recipe/Parts/Assets.vue";
|
import Assets from "@/components/Recipe/Parts/Assets.vue";
|
||||||
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
||||||
|
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BulkAdd,
|
BulkAdd,
|
||||||
@ -133,6 +139,7 @@ export default {
|
|||||||
Ingredients,
|
Ingredients,
|
||||||
Assets,
|
Assets,
|
||||||
Notes,
|
Notes,
|
||||||
|
SettingsMenu,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: Object,
|
value: Object,
|
||||||
|
@ -3,13 +3,10 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
from mealie.core import root_logger
|
from mealie.core import root_logger
|
||||||
from mealie.core.config import APP_VERSION, settings
|
from mealie.core.config import APP_VERSION, settings
|
||||||
from mealie.routes import (backup_routes, debug_routes, migration_routes,
|
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
|
||||||
theme_routes, utility_routes)
|
|
||||||
from mealie.routes.groups import groups
|
from mealie.routes.groups import groups
|
||||||
from mealie.routes.mealplans import mealplans
|
from mealie.routes.mealplans import mealplans
|
||||||
from mealie.routes.recipe import (all_recipe_routes, category_routes,
|
from mealie.routes.recipe import router as recipe_router
|
||||||
recipe_assets, recipe_crud_routes,
|
|
||||||
tag_routes)
|
|
||||||
from mealie.routes.site_settings import all_settings
|
from mealie.routes.site_settings import all_settings
|
||||||
from mealie.routes.users import users
|
from mealie.routes.users import users
|
||||||
|
|
||||||
@ -30,15 +27,10 @@ def start_scheduler():
|
|||||||
|
|
||||||
def api_routers():
|
def api_routers():
|
||||||
# Authentication
|
# Authentication
|
||||||
app.include_router(utility_routes.router)
|
|
||||||
app.include_router(users.router)
|
app.include_router(users.router)
|
||||||
app.include_router(groups.router)
|
app.include_router(groups.router)
|
||||||
# Recipes
|
# Recipes
|
||||||
app.include_router(all_recipe_routes.router)
|
app.include_router(recipe_router)
|
||||||
app.include_router(category_routes.router)
|
|
||||||
app.include_router(tag_routes.router)
|
|
||||||
app.include_router(recipe_crud_routes.router)
|
|
||||||
app.include_router(recipe_assets.router)
|
|
||||||
# Meal Routes
|
# Meal Routes
|
||||||
app.include_router(mealplans.router)
|
app.include_router(mealplans.router)
|
||||||
# Settings Routes
|
# Settings Routes
|
||||||
@ -49,6 +41,7 @@ def api_routers():
|
|||||||
# Migration Routes
|
# Migration Routes
|
||||||
app.include_router(migration_routes.router)
|
app.include_router(migration_routes.router)
|
||||||
app.include_router(debug_routes.router)
|
app.include_router(debug_routes.router)
|
||||||
|
app.include_router(utility_routes.router)
|
||||||
|
|
||||||
|
|
||||||
api_routers()
|
api_routers()
|
||||||
|
@ -120,7 +120,7 @@ class AppSettings(BaseSettings):
|
|||||||
DEFAULT_EMAIL: str = "changeme@email.com"
|
DEFAULT_EMAIL: str = "changeme@email.com"
|
||||||
DEFAULT_PASSWORD: str = "MyPassword"
|
DEFAULT_PASSWORD: str = "MyPassword"
|
||||||
|
|
||||||
TOKEN_TIME: int = 2 # Time in Hours
|
TOKEN_TIME: int = 2 # Time in Hours
|
||||||
|
|
||||||
# Not Used!
|
# Not Used!
|
||||||
SFTP_USERNAME: Optional[str]
|
SFTP_USERNAME: Optional[str]
|
||||||
|
@ -14,7 +14,7 @@ ALGORITHM = "HS256"
|
|||||||
def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
|
def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
|
||||||
to_encode = data.copy()
|
to_encode = data.copy()
|
||||||
expires_delta = expires_delta or timedelta(hours=settings.TOKEN_TIME)
|
expires_delta = expires_delta or timedelta(hours=settings.TOKEN_TIME)
|
||||||
|
|
||||||
expire = datetime.utcnow() + expires_delta
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
|
||||||
to_encode.update({"exp": expire})
|
to_encode.update({"exp": expire})
|
||||||
|
@ -137,4 +137,3 @@ class BaseDocument:
|
|||||||
|
|
||||||
session.delete(result)
|
session.delete(result)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import operator
|
import operator
|
||||||
import shutil
|
import shutil
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.core.security import create_file_token
|
from mealie.core.security import create_file_token
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user, validate_file_token
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
||||||
from mealie.services.backups import imports
|
from mealie.services.backups import imports
|
||||||
from mealie.services.backups.exports import backup_all
|
from mealie.services.backups.exports import backup_all
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from starlette.responses import FileResponse
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
|
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
|
||||||
|
|
||||||
@ -46,8 +44,8 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
|
|||||||
export_groups=data.options.groups,
|
export_groups=data.options.groups,
|
||||||
)
|
)
|
||||||
return {"export_path": export_path}
|
return {"export_path": export_path}
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/upload", status_code=status.HTTP_200_OK)
|
@router.post("/upload", status_code=status.HTTP_200_OK)
|
||||||
@ -59,8 +57,7 @@ def upload_backup_file(archive: UploadFile = File(...)):
|
|||||||
shutil.copyfileobj(archive.file, buffer)
|
shutil.copyfileobj(archive.file, buffer)
|
||||||
|
|
||||||
if not dest.is_file:
|
if not dest.is_file:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{file_name}/download")
|
@router.get("/{file_name}/download")
|
||||||
@ -95,8 +92,8 @@ def delete_backup(file_name: str):
|
|||||||
file_path = app_dirs.BACKUP_DIR.joinpath(file_name)
|
file_path = app_dirs.BACKUP_DIR.joinpath(file_name)
|
||||||
|
|
||||||
if not file_path.is_file():
|
if not file_path.is_file():
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
try:
|
try:
|
||||||
file_path.unlink()
|
file_path.unlink()
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from mealie.core.config import APP_VERSION, app_dirs, settings
|
from mealie.core.config import APP_VERSION, app_dirs, settings
|
||||||
from mealie.core.root_logger import LOGGER_FILE
|
from mealie.core.root_logger import LOGGER_FILE
|
||||||
|
@ -39,8 +39,8 @@ async def create_group(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
db.groups.create(session, group_data.dict())
|
db.groups.create(session, group_data.dict())
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}")
|
@router.put("/{id}")
|
||||||
@ -61,23 +61,14 @@ async def delete_user_group(
|
|||||||
""" Removes a user group from the database """
|
""" Removes a user group from the database """
|
||||||
|
|
||||||
if id == 1:
|
if id == 1:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="DEFAULT_GROUP")
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
|
||||||
detail='DEFAULT_GROUP'
|
|
||||||
)
|
|
||||||
|
|
||||||
group: GroupInDB = db.groups.get(session, id)
|
group: GroupInDB = db.groups.get(session, id)
|
||||||
|
|
||||||
if not group:
|
if not group:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_NOT_FOUND")
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
|
||||||
detail='GROUP_NOT_FOUND'
|
|
||||||
)
|
|
||||||
|
|
||||||
if not group.users == []:
|
if not group.users == []:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_WITH_USERS")
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
|
||||||
detail='GROUP_WITH_USERS'
|
|
||||||
)
|
|
||||||
|
|
||||||
db.groups.delete(session, id)
|
db.groups.delete(session, id)
|
||||||
|
@ -43,8 +43,8 @@ def update_meal_plan(
|
|||||||
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
||||||
try:
|
try:
|
||||||
db.meals.update(session, plan_id, processed_plan.dict())
|
db.meals.update(session, plan_id, processed_plan.dict())
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{plan_id}")
|
@router.delete("/{plan_id}")
|
||||||
@ -53,8 +53,8 @@ def delete_meal_plan(plan_id, session: Session = Depends(generate_session), curr
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
db.meals.delete(session, plan_id)
|
db.meals.delete(session, plan_id)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/this-week", response_model=MealPlanInDB)
|
@router.get("/this-week", response_model=MealPlanInDB)
|
||||||
@ -90,8 +90,8 @@ def get_todays_image(session: Session = Depends(generate_session), group_name: s
|
|||||||
if recipe:
|
if recipe:
|
||||||
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
||||||
else:
|
else:
|
||||||
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
if recipe_image:
|
if recipe_image:
|
||||||
return FileResponse(recipe_image)
|
return FileResponse(recipe_image)
|
||||||
else:
|
else:
|
||||||
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
@ -9,6 +9,7 @@ from mealie.routes.deps import get_current_user
|
|||||||
from mealie.schema.migration import MigrationFile, Migrations
|
from mealie.schema.migration import MigrationFile, Migrations
|
||||||
from mealie.services.migrations import migration
|
from mealie.services.migrations import migration
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/migrations", tags=["Migration"], dependencies=[Depends(get_current_user)])
|
router = APIRouter(prefix="/api/migrations", tags=["Migration"], dependencies=[Depends(get_current_user)])
|
||||||
|
|
||||||
@ -52,8 +53,7 @@ def delete_migration_data(import_type: migration.Migration, file_name: str):
|
|||||||
elif remove_path.is_dir():
|
elif remove_path.is_dir():
|
||||||
shutil.rmtree(remove_path)
|
shutil.rmtree(remove_path)
|
||||||
else:
|
else:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{import_type}/upload", status_code=status.HTTP_200_OK)
|
@router.post("/{import_type}/upload", status_code=status.HTTP_200_OK)
|
||||||
@ -67,4 +67,4 @@ def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFi
|
|||||||
shutil.copyfileobj(archive.file, buffer)
|
shutil.copyfileobj(archive.file, buffer)
|
||||||
|
|
||||||
if not dest.is_file:
|
if not dest.is_file:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_assets, recipe_crud_routes, tag_routes
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
router.include_router(all_recipe_routes.router)
|
||||||
|
router.include_router(recipe_crud_routes.router)
|
||||||
|
router.include_router(recipe_assets.router)
|
||||||
|
router.include_router(category_routes.router)
|
||||||
|
router.include_router(tag_routes.router)
|
@ -1,9 +1,7 @@
|
|||||||
from typing import List, Optional
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.schema.recipe import AllRecipeRequest, RecipeSummary
|
from mealie.schema.recipe import RecipeSummary
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -31,66 +29,6 @@ async def get_recipe_summary(
|
|||||||
return db.recipes.get_all(session, limit=limit, start=start, override_schema=RecipeSummary)
|
return db.recipes.get_all(session, limit=limit, start=start, override_schema=RecipeSummary)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/recipes", deprecated=True)
|
|
||||||
def get_all_recipes(
|
|
||||||
keys: Optional[List[str]] = Query(...),
|
|
||||||
num: Optional[int] = 100,
|
|
||||||
session: Session = Depends(generate_session),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Returns key data for all recipes based off the query paramters provided.
|
|
||||||
For example, if slug, image, and name are provided you will recieve a list of
|
|
||||||
recipes containing the slug, image, and name property. By default, responses
|
|
||||||
are limited to 100.
|
|
||||||
|
|
||||||
At this time you can only query top level values:
|
|
||||||
|
|
||||||
- slug
|
|
||||||
- name
|
|
||||||
- description
|
|
||||||
- image
|
|
||||||
- recipeYield
|
|
||||||
- total_time
|
|
||||||
- prep_time
|
|
||||||
- perform_time
|
|
||||||
- rating
|
|
||||||
- org_url
|
|
||||||
|
|
||||||
**Note:** You may experience problems with with query parameters. As an alternative
|
|
||||||
you may also use the post method and provide a body.
|
|
||||||
See the *Post* method for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return db.recipes.get_all_limit_columns(session, keys, limit=num)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/recipes", deprecated=True)
|
|
||||||
def get_all_recipes_post(body: AllRecipeRequest, session: Session = Depends(generate_session)):
|
|
||||||
"""
|
|
||||||
Returns key data for all recipes based off the body data provided.
|
|
||||||
For example, if slug, image, and name are provided you will recieve a list of
|
|
||||||
recipes containing the slug, image, and name property.
|
|
||||||
|
|
||||||
At this time you can only query top level values:
|
|
||||||
|
|
||||||
- slug
|
|
||||||
- name
|
|
||||||
- description
|
|
||||||
- image
|
|
||||||
- recipeYield
|
|
||||||
- total_time
|
|
||||||
- prep_time
|
|
||||||
- perform_time
|
|
||||||
- rating
|
|
||||||
- org_url
|
|
||||||
|
|
||||||
Refer to the body example for data formats.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
return db.recipes.get_all_limit_columns(session, body.properties, body.limit)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/recipes/category")
|
@router.post("/api/recipes/category")
|
||||||
def filter_by_category(categories: list, session: Session = Depends(generate_session)):
|
def filter_by_category(categories: list, session: Session = Depends(generate_session)):
|
||||||
""" pass a list of categories and get a list of recipes associated with those categories """
|
""" pass a list of categories and get a list of recipes associated with those categories """
|
||||||
|
@ -37,8 +37,8 @@ async def create_recipe_category(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return db.categories.create(session, category.dict())
|
return db.categories.create(session, category.dict())
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
||||||
@ -52,8 +52,8 @@ async def update_recipe_category(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return db.categories.update(session, category, new_category.dict())
|
return db.categories.update(session, category, new_category.dict())
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{category}")
|
@router.delete("/{category}")
|
||||||
@ -66,5 +66,5 @@ async def delete_recipe_category(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
db.categories.delete(session, category)
|
db.categories.delete(session, category)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import shutil
|
import shutil
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, status, HTTPException
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
||||||
from fastapi.datastructures import UploadFile
|
from fastapi.datastructures import UploadFile
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
@ -11,7 +12,24 @@ from slugify import slugify
|
|||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/recipes", tags=["Recipe Assets"])
|
router = APIRouter(prefix="/api/recipes", tags=["Recipe Media"])
|
||||||
|
|
||||||
|
|
||||||
|
class ImageType(str, Enum):
|
||||||
|
original = "original.webp"
|
||||||
|
small = "min-original.webp"
|
||||||
|
tiny = "tiny-original.webp"
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/image/{recipe_slug}/{file_name}")
|
||||||
|
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
||||||
|
"""Takes in a recipe slug, returns the static image. This route is proxied in the docker image
|
||||||
|
and should not hit the API in production"""
|
||||||
|
recipe_image = app_dirs.IMG_DIR.joinpath(recipe_slug, file_name.value)
|
||||||
|
if recipe_image:
|
||||||
|
return FileResponse(recipe_image)
|
||||||
|
else:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{recipe_slug}/asset")
|
@router.get("/{recipe_slug}/asset")
|
||||||
@ -41,9 +59,9 @@ def upload_recipe_asset(
|
|||||||
shutil.copyfileobj(file.file, buffer)
|
shutil.copyfileobj(file.file, buffer)
|
||||||
|
|
||||||
if not dest.is_file():
|
if not dest.is_file():
|
||||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
recipe: Recipe = db.recipes.get(session, recipe_slug)
|
recipe: Recipe = db.recipes.get(session, recipe_slug)
|
||||||
recipe.assets.append(asset_in)
|
recipe.assets.append(asset_in)
|
||||||
db.recipes.update(session, recipe_slug, recipe.dict())
|
db.recipes.update(session, recipe_slug, recipe.dict())
|
||||||
return asset_in
|
return asset_in
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
from enum import Enum
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
||||||
from fastapi.responses import FileResponse
|
|
||||||
from mealie.core.config import app_dirs
|
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.recipe import Recipe, RecipeURLIn
|
from mealie.schema.recipe import Recipe, RecipeURLIn
|
||||||
from mealie.services.image.image import IMG_OPTIONS, delete_image, read_image, rename_image, scrape_image, write_image
|
from mealie.services.image.image import delete_image, rename_image, scrape_image, write_image
|
||||||
from mealie.services.scraper.scraper import create_from_url
|
from mealie.services.scraper.scraper import create_from_url
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
@ -101,25 +97,8 @@ def delete_recipe(
|
|||||||
try:
|
try:
|
||||||
db.recipes.delete(session, recipe_slug)
|
db.recipes.delete(session, recipe_slug)
|
||||||
delete_image(recipe_slug)
|
delete_image(recipe_slug)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ImageType(str, Enum):
|
|
||||||
original = "original.webp"
|
|
||||||
small = "min-original.webp"
|
|
||||||
tiny = "tiny-original.webp"
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/image/{recipe_slug}/{file_name}")
|
|
||||||
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
|
||||||
""" Takes in a recipe slug, returns the static image """
|
|
||||||
recipe_image = app_dirs.IMG_DIR.joinpath(recipe_slug, file_name.value)
|
|
||||||
if recipe_image:
|
|
||||||
return FileResponse(recipe_image)
|
|
||||||
else:
|
|
||||||
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{recipe_slug}/image")
|
@router.put("/{recipe_slug}/image")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
@ -7,10 +7,7 @@ from sqlalchemy.orm.session import Session
|
|||||||
|
|
||||||
router = APIRouter(tags=["Recipes"])
|
router = APIRouter(tags=["Recipes"])
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(prefix="/api/tags", tags=["Recipe Tags"])
|
||||||
prefix="/api/tags",
|
|
||||||
tags=["Recipe Tags"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("")
|
@router.get("")
|
||||||
@ -59,5 +56,5 @@ async def delete_recipe_tag(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
db.tags.delete(session, tag)
|
db.tags.delete(session, tag)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -21,7 +21,6 @@ def create_theme(data: SiteTheme, session: Session = Depends(generate_session),
|
|||||||
db.themes.create(session, data.dict())
|
db.themes.create(session, data.dict())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/themes/{theme_name}")
|
@router.get("/themes/{theme_name}")
|
||||||
def get_single_theme(theme_name: str, session: Session = Depends(generate_session)):
|
def get_single_theme(theme_name: str, session: Session = Depends(generate_session)):
|
||||||
""" Returns a named theme """
|
""" Returns a named theme """
|
||||||
@ -44,5 +43,5 @@ def delete_theme(theme_name: str, session: Session = Depends(generate_session),
|
|||||||
""" Deletes theme from the database """
|
""" Deletes theme from the database """
|
||||||
try:
|
try:
|
||||||
db.themes.delete(session, theme_name)
|
db.themes.delete(session, theme_name)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, status
|
from fastapi import APIRouter, Depends, status
|
||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import shutil
|
import shutil
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException
|
from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
@ -34,8 +33,8 @@ async def get_all_users(
|
|||||||
):
|
):
|
||||||
|
|
||||||
if not current_user.admin:
|
if not current_user.admin:
|
||||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
return db.users.get_all(session)
|
return db.users.get_all(session)
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +66,6 @@ async def reset_user_password(
|
|||||||
db.users.update_password(session, id, new_password)
|
db.users.update_password(session, id, new_password)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}")
|
@router.put("/{id}")
|
||||||
async def update_user(
|
async def update_user(
|
||||||
id: int,
|
id: int,
|
||||||
@ -109,7 +107,7 @@ async def update_user_image(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
[x.unlink() for x in app_dirs.USER_DIR.join(id).glob("profile_image.*")]
|
[x.unlink() for x in app_dirs.USER_DIR.join(id).glob("profile_image.*")]
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
dest = app_dirs.USER_DIR.joinpath(id, f"profile_image.{extension}")
|
dest = app_dirs.USER_DIR.joinpath(id, f"profile_image.{extension}")
|
||||||
@ -118,7 +116,7 @@ async def update_user_image(
|
|||||||
shutil.copyfileobj(profile_image.file, buffer)
|
shutil.copyfileobj(profile_image.file, buffer)
|
||||||
|
|
||||||
if not dest.is_file:
|
if not dest.is_file:
|
||||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}/password")
|
@router.put("/{id}/password")
|
||||||
@ -133,12 +131,11 @@ async def update_password(
|
|||||||
match_passwords = verify_password(password_change.current_password, current_user.password)
|
match_passwords = verify_password(password_change.current_password, current_user.password)
|
||||||
match_id = current_user.id == id
|
match_id = current_user.id == id
|
||||||
|
|
||||||
if not ( match_passwords and match_id ):
|
if not (match_passwords and match_id):
|
||||||
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
new_password = get_password_hash(password_change.new_password)
|
new_password = get_password_hash(password_change.new_password)
|
||||||
db.users.update_password(session, id, new_password)
|
db.users.update_password(session, id, new_password)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{id}")
|
@router.delete("/{id}")
|
||||||
@ -150,13 +147,10 @@ async def delete_user(
|
|||||||
""" Removes a user from the database. Must be the current user or a super user"""
|
""" Removes a user from the database. Must be the current user or a super user"""
|
||||||
|
|
||||||
if id == 1:
|
if id == 1:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
|
||||||
detail='SUPER_USER'
|
|
||||||
)
|
|
||||||
|
|
||||||
if current_user.id == id or current_user.admin:
|
if current_user.id == id or current_user.admin:
|
||||||
try:
|
try:
|
||||||
db.users.delete(session, id)
|
db.users.delete(session, id)
|
||||||
except:
|
except Exception:
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -8,6 +8,7 @@ from mealie.routes.deps import get_current_user
|
|||||||
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
||||||
from mealie.schema.user import UserIn, UserInDB
|
from mealie.schema.user import UserIn, UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])
|
router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ async def create_user_sign_up_key(
|
|||||||
""" Generates a Random Token that a new user can sign up with """
|
""" Generates a Random Token that a new user can sign up with """
|
||||||
|
|
||||||
if not current_user.admin:
|
if not current_user.admin:
|
||||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
sign_up = {
|
sign_up = {
|
||||||
"token": str(uuid.uuid1().hex),
|
"token": str(uuid.uuid1().hex),
|
||||||
@ -43,7 +44,6 @@ async def create_user_sign_up_key(
|
|||||||
return db.sign_ups.create(session, sign_up)
|
return db.sign_ups.create(session, sign_up)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{token}")
|
@router.post("/{token}")
|
||||||
async def create_user_with_token(
|
async def create_user_with_token(
|
||||||
token: str,
|
token: str,
|
||||||
@ -55,12 +55,12 @@ async def create_user_with_token(
|
|||||||
# Validate Token
|
# Validate Token
|
||||||
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
|
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
|
||||||
if not db_entry:
|
if not db_entry:
|
||||||
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
# Create User
|
# Create User
|
||||||
new_user.admin = db_entry.admin
|
new_user.admin = db_entry.admin
|
||||||
new_user.password = get_password_hash(new_user.password)
|
new_user.password = get_password_hash(new_user.password)
|
||||||
data = db.users.create(session, new_user.dict())
|
db.users.create(session, new_user.dict())
|
||||||
|
|
||||||
# DeleteToken
|
# DeleteToken
|
||||||
db.sign_ups.delete(session, token)
|
db.sign_ups.delete(session, token)
|
||||||
@ -74,6 +74,6 @@ async def delete_token(
|
|||||||
):
|
):
|
||||||
""" Removed a token from the database """
|
""" Removed a token from the database """
|
||||||
if not current_user.admin:
|
if not current_user.admin:
|
||||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
db.sign_ups.delete(session, token)
|
db.sign_ups.delete(session, token)
|
||||||
|
@ -4,16 +4,17 @@ from typing import Optional
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from mealie.routes.deps import validate_file_token
|
from mealie.routes.deps import validate_file_token
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/download")
|
@router.get("/download")
|
||||||
async def download_file(file_path: Optional[Path] = Depends(validate_file_token)):
|
async def download_file(file_path: Optional[Path] = Depends(validate_file_token)):
|
||||||
""" Uses a file token obtained by an active user to retrieve a file from the operating
|
"""Uses a file token obtained by an active user to retrieve a file from the operating
|
||||||
system. """
|
system."""
|
||||||
print("File Name:", file_path)
|
print("File Name:", file_path)
|
||||||
if not file_path.is_file():
|
if not file_path.is_file():
|
||||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)
|
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)
|
||||||
|
@ -37,7 +37,7 @@ class ExportDatabase:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates]
|
self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates]
|
||||||
except:
|
except Exception:
|
||||||
self.templates = False
|
self.templates = False
|
||||||
logger.info("No Jinja2 Templates Registered for Export")
|
logger.info("No Jinja2 Templates Registered for Export")
|
||||||
|
|
||||||
|
@ -84,20 +84,20 @@ class ImportDatabase:
|
|||||||
try:
|
try:
|
||||||
del recipe_dict["_id"]
|
del recipe_dict["_id"]
|
||||||
del recipe_dict["date_added"]
|
del recipe_dict["date_added"]
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Migration from list to Object Type Data
|
# Migration from list to Object Type Data
|
||||||
try:
|
try:
|
||||||
if "" in recipe_dict["tags"]:
|
if "" in recipe_dict["tags"]:
|
||||||
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
|
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "" in recipe_dict["categories"]:
|
if "" in recipe_dict["categories"]:
|
||||||
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""]
|
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""]
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if type(recipe_dict["extras"]) == list:
|
if type(recipe_dict["extras"]) == list:
|
||||||
|
@ -54,7 +54,7 @@ def rename_image(original_slug, new_slug) -> Path:
|
|||||||
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
||||||
try:
|
try:
|
||||||
delete_image(recipe_slug)
|
delete_image(recipe_slug)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
image_dir = Path(app_dirs.IMG_DIR.joinpath(f"{recipe_slug}"))
|
image_dir = Path(app_dirs.IMG_DIR.joinpath(f"{recipe_slug}"))
|
||||||
@ -100,7 +100,7 @@ def scrape_image(image_url: str, slug: str) -> Path:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(image_url, stream=True)
|
r = requests.get(image_url, stream=True)
|
||||||
except:
|
except Exception:
|
||||||
logger.exception("Fatal Image Request Exception")
|
logger.exception("Fatal Image Request Exception")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -35,14 +35,15 @@ def minify_image(image_file: Path) -> ImageSizes:
|
|||||||
min_dest (Path): FULL Destination File Path
|
min_dest (Path): FULL Destination File Path
|
||||||
tiny_dest (Path): FULL Destination File Path
|
tiny_dest (Path): FULL Destination File Path
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def cleanup(dir: Path) -> None:
|
def cleanup(dir: Path) -> None:
|
||||||
for file in dir.glob("*.*"):
|
for file in dir.glob("*.*"):
|
||||||
if file.suffix != ".webp":
|
if file.suffix != ".webp":
|
||||||
file.unlink()
|
file.unlink()
|
||||||
|
|
||||||
org_dest = image_file.parent.joinpath(f"original.webp")
|
org_dest = image_file.parent.joinpath("original.webp")
|
||||||
min_dest = image_file.parent.joinpath(f"min-original.webp")
|
min_dest = image_file.parent.joinpath("min-original.webp")
|
||||||
tiny_dest = image_file.parent.joinpath(f"tiny-original.webp")
|
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
||||||
|
|
||||||
if min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
if min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
||||||
return
|
return
|
||||||
|
@ -24,7 +24,7 @@ def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProce
|
|||||||
description=recipe.description,
|
description=recipe.description,
|
||||||
)
|
)
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
|
|
||||||
meal_data = MealOut(
|
meal_data = MealOut(
|
||||||
date=meal_plan_base.startDate + timedelta(days=x),
|
date=meal_plan_base.startDate + timedelta(days=x),
|
||||||
|
@ -8,7 +8,7 @@ from mealie.core import root_logger
|
|||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.schema.migration import MigrationImport
|
from mealie.schema.migration import MigrationImport
|
||||||
from mealie.schema.recipe import Recipe
|
from mealie.schema.recipe import Recipe
|
||||||
from mealie.services.image import image, minify
|
from mealie.services.image import image
|
||||||
from mealie.services.scraper.cleaner import Cleaner
|
from mealie.services.scraper.cleaner import Cleaner
|
||||||
from mealie.utils.unzip import unpack_zip
|
from mealie.utils.unzip import unpack_zip
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
@ -115,7 +115,7 @@ class Cleaner:
|
|||||||
for step in instructions
|
for step in instructions
|
||||||
if step["type"].find("HowToStep") > -1
|
if step["type"].find("HowToStep") > -1
|
||||||
]
|
]
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -21,7 +21,7 @@ def basic_recipe_from_opengraph(html: str, url: str) -> dict:
|
|||||||
data = extruct.extract(html, base_url=base_url)
|
data = extruct.extract(html, base_url=base_url)
|
||||||
try:
|
try:
|
||||||
properties = data["opengraph"][0]["properties"]
|
properties = data["opengraph"][0]["properties"]
|
||||||
except:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -67,7 +67,7 @@ def download_image_for_recipe(recipe: dict) -> dict:
|
|||||||
try:
|
try:
|
||||||
img_path = scrape_image(recipe.get("image"), recipe.get("slug"))
|
img_path = scrape_image(recipe.get("image"), recipe.get("slug"))
|
||||||
recipe["image"] = img_path.name
|
recipe["image"] = img_path.name
|
||||||
except:
|
except Exception:
|
||||||
recipe["image"] = "no image"
|
recipe["image"] = "no image"
|
||||||
|
|
||||||
return recipe
|
return recipe
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from mealie.core.config import app_dirs, settings
|
from mealie.core.config import app_dirs, settings
|
||||||
|
|
||||||
#! I don't like it either!
|
# ! I don't like it either!
|
||||||
SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath("test.db")
|
SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath("test.db")
|
||||||
SQLITE_FILE.unlink(missing_ok=True)
|
SQLITE_FILE.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
BIN
tests/data/migrations/chowdown/chowdown-gh-pages.zip
Normal file
BIN
tests/data/migrations/chowdown/chowdown-gh-pages.zip
Normal file
Binary file not shown.
BIN
tests/data/migrations/nextcloud/new_nextcloud.zip
Normal file
BIN
tests/data/migrations/nextcloud/new_nextcloud.zip
Normal file
Binary file not shown.
@ -37,7 +37,7 @@ def test_update_group(api_client: TestClient, api_routes: AppRoutes, token):
|
|||||||
# Test Update
|
# Test Update
|
||||||
response = api_client.put(api_routes.groups_id(2), json=new_data, headers=token)
|
response = api_client.put(api_routes.groups_id(2), json=new_data, headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Validate Changes
|
# Validate Changes
|
||||||
response = api_client.get(api_routes.groups, headers=token)
|
response = api_client.get(api_routes.groups, headers=token)
|
||||||
all_groups = json.loads(response.text)
|
all_groups = json.loads(response.text)
|
||||||
|
@ -34,11 +34,6 @@ def test_create_no_image(api_client: TestClient, api_routes: AppRoutes, token, r
|
|||||||
assert json.loads(response.text) == "banana-bread-no-image"
|
assert json.loads(response.text) == "banana-bread-no-image"
|
||||||
|
|
||||||
|
|
||||||
def test_read_all_post(api_client: TestClient, api_routes: AppRoutes):
|
|
||||||
response = api_client.post(api_routes.recipes, json={"properties": ["slug", "description", "rating"]})
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("recipe_data", recipe_test_data)
|
@pytest.mark.parametrize("recipe_data", recipe_test_data)
|
||||||
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data, token):
|
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data, token):
|
||||||
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
|
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user