Feature/additional endpoints (#257)

* new recipe summary route

* add categories to cards

* add pillow

* show tags instead of categories

* additional debug info

* add todays meal image url

* about page

* fix reactive tag

* changelog + docs

* bump version

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-04-03 11:25:57 -08:00 committed by GitHub
parent 4c3f751e80
commit 764f85fb40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 330 additions and 119 deletions

View file

@ -13,4 +13,10 @@
- Random meal-planner will no longer duplicate recipes unless no other options
- New Quick Week button to generate next 5 day week of recipe slots.
- Minor UI tweaks
- Recipe Cards now display 2 recipe tags
- Recipe images are now minified. This comes with a serious performance improvement. On initial startup you may experience some delays. Images are migrated to the new structure on startup, depending on the size of your database this can take some time.
- Note that original images are still kept for large displays like on the individual recipe pages.
- A smaller image is used for recipe cards
- A 'tiny' image is used for search images.

File diff suppressed because one or more lines are too long

View file

@ -74,6 +74,7 @@ nav:
- Guidelines: "contributors/developers-guide/general-guidelines.md"
- Development Road Map: "roadmap.md"
- Change Log:
- v0.4.1 Frontend/UI: "changelog/v0.4.1.md"
- v0.4.0 Authentication: "changelog/v0.4.0.md"
- v0.3.0 Improvements: "changelog/v0.3.0.md"
- v0.2.0 Now With Tests!: "changelog/v0.2.0.md"

View file

@ -5,6 +5,7 @@ const prefix = baseURL + "debug";
const debugURLs = {
version: `${prefix}/version`,
debug: `${prefix}`,
lastRecipe: `${prefix}/last-recipe-json`,
demo: `${prefix}/is-demo`,
};
@ -14,6 +15,12 @@ export const metaAPI = {
let response = await apiReq.get(debugURLs.version);
return response.data;
},
async getDebugInfo() {
const response = await apiReq.get(debugURLs.debug);
return response.data;
},
async getLastJson() {
let response = await apiReq.get(debugURLs.lastRecipe);
return response.data;

View file

@ -8,6 +8,7 @@ const prefix = baseURL + "recipes/";
const recipeURLs = {
allRecipes: baseURL + "recipes",
summary: baseURL + "recipes" + "/summary",
allRecipesByCategory: prefix + "category",
create: prefix + "create",
createByURL: prefix + "create-url",
@ -86,6 +87,11 @@ export const recipeAPI = {
return response.data;
},
async allSummary() {
const response = await apiReq.get(recipeURLs.summary);
return response.data;
},
recipeImage(recipeSlug) {
return `/api/recipes/${recipeSlug}/image?image_type=original`;
},

View file

@ -74,7 +74,7 @@
</v-list>
<v-list nav dense class="fixedBottom">
<v-list-item href="">
<v-list-item to="/admin/about">
<v-list-item-icon class="mr-3 pt-1">
<v-icon :color="newVersionAvailable ? 'red--text' : ''">
mdi-information
@ -87,6 +87,7 @@
</v-list-item-title>
<v-list-item-subtitle>
<a
@click.prevent
href="https://github.com/hay-kot/mealie/releases/latest"
target="_blank"
:class="newVersionAvailable ? 'red--text' : 'green--text'"

View file

@ -5,6 +5,7 @@
:elevation="hover ? 12 : 2"
:to="route ? `/recipe/${slug}` : ''"
@click="$emit('click')"
min-height="275"
>
<v-img height="200" :src="getImage(image)">
<v-expand-transition v-if="description">
@ -25,9 +26,9 @@
</div>
</v-card-title>
<v-card-actions class="">
<v-card-actions>
<v-rating
class="mr-2"
class="mr-2 my-auto"
color="secondary"
background-color="secondary lighten-3"
dense
@ -36,14 +37,25 @@
:value="rating"
></v-rating>
<v-spacer></v-spacer>
<RecipeChips
:items="tags"
:title="false"
:limit="2"
:small="true"
:isCategory="false"
/>
</v-card-actions>
</v-card>
</v-hover>
</template>
<script>
import RecipeChips from "@/components/Recipe/RecipeViewer/RecipeChips";
import { api } from "@/api";
export default {
components: {
RecipeChips,
},
props: {
name: String,
slug: String,
@ -54,6 +66,9 @@ export default {
route: {
default: true,
},
tags: {
default: true,
},
},
methods: {
getImage(image) {

View file

@ -1,12 +1,13 @@
<template>
<div v-if="items && items.length > 0">
<h2 class="mt-4">{{ title }}</h2>
<h2 v-if="title" class="mt-4">{{ title }}</h2>
<v-chip
label
class="ma-1"
color="accent"
:small="small"
dark
v-for="category in items"
v-for="category in items.slice(0, limit)"
:to="`/recipes/${urlParam}/${getSlug(category)}`"
:key="category"
>
@ -19,10 +20,18 @@
export default {
props: {
items: Array,
title: String,
title: {
default: null,
},
isCategory: {
default: true,
},
limit: {
default: 999,
},
small: {
default: false,
},
},
computed: {
allCategories() {
@ -32,8 +41,8 @@ export default {
return this.$store.getters.getAllTags;
},
urlParam() {
return this.isCategory ? 'category' : 'tag'
}
return this.isCategory ? "category" : "tag";
},
},
methods: {
getSlug(name) {

View file

@ -15,9 +15,9 @@
<v-menu offset-y v-if="sortable">
<template v-slot:activator="{ on, attrs }">
<v-btn-toggle group>
<v-btn text v-bind="attrs" v-on="on">{{
$t("general.sort")
}}</v-btn>
<v-btn text v-bind="attrs" v-on="on">
{{ $t("general.sort") }}
</v-btn>
</v-btn-toggle>
</template>
<v-list>
@ -53,6 +53,7 @@
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
/>
</v-col>
</v-row>

View file

@ -0,0 +1,91 @@
<template>
<div>
<v-card class="mt-3">
<v-card-title class="headline">
About Mealie
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-list-item-group color="primary">
<v-list-item v-for="property in prettyInfo" :key="property.name">
<v-list-item-icon>
<v-icon> {{ property.icon || "mdi-account" }} </v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title class="pl-4 flex row justify-space-between">
<div>{{ property.name }}</div>
<div>{{ property.value }}</div>
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-card-text>
<v-divider></v-divider>
</v-card>
</div>
</template>
<script>
import { api } from "@/api";
export default {
data() {
return {
prettyInfo: [],
};
},
async mounted() {
await this.getInfo();
},
methods: {
async getInfo() {
const debugInfo = await api.meta.getDebugInfo();
this.prettyInfo = [
{
name: "Version",
icon: "mdi-information",
value: debugInfo.version,
},
{
name: "Application Mode",
icon: "mdi-dev-to",
value: debugInfo.production ? "Production" : "Development",
},
{
name: "Demo Status",
icon: "mdi-test-tube",
value: debugInfo.demoStatus ? "Demo" : "Not Demo",
},
{
name: "API Port",
icon: "mdi-api",
value: debugInfo.apiPort,
},
{
name: "API Docs",
icon: "mdi-file-document",
value: debugInfo.apiDocs ? "Enabled" : "Disabled",
},
{
name: "Database Type",
icon: "mdi-database",
value: debugInfo.dbType,
},
{
name: "SQLite File",
icon: "mdi-file-cabinet",
value: debugInfo.sqliteFile,
},
{
name: "Default Group",
icon: "mdi-account-group",
value: debugInfo.defaultGroup,
},
];
},
},
};
</script>
<style lang="scss" scoped>
</style>

View file

@ -33,7 +33,7 @@ export default {
},
},
watch: {
async currentCategory() {
async currentTag() {
this.getRecipes();
},
},

View file

@ -6,6 +6,7 @@ import Migration from "@/pages/Admin/Migration";
import Profile from "@/pages/Admin/Profile";
import ManageUsers from "@/pages/Admin/ManageUsers";
import Settings from "@/pages/Admin/Settings";
import About from "@/pages/Admin/About";
import { store } from "../store";
export default {
@ -50,5 +51,9 @@ export default {
path: "settings",
component: Settings,
},
{
path: "about",
component: About,
},
],
};

View file

@ -54,15 +54,15 @@ const store = new Vuex.Store({
actions: {
async requestRecentRecipes() {
const keys = [
"name",
"slug",
"image",
"description",
"dateAdded",
"rating",
];
const payload = await api.recipes.allByKeys(keys);
// const keys = [
// "name",
// "slug",
// "image",
// "description",
// "dateAdded",
// "rating",
// ];
const payload = await api.recipes.allSummary();
this.commit("setRecentRecipes", payload);
},

View file

@ -5,7 +5,7 @@ from typing import Optional, Union
from pydantic import BaseSettings, Field, validator
APP_VERSION = "v0.4.0"
APP_VERSION = "v0.4.1"
DB_VERSION = "v0.4.0"
CWD = Path(__file__).parent

View file

@ -15,17 +15,10 @@ class BaseDocument:
self.schema: BaseModel
# TODO: Improve Get All Query Functionality
def get_all(self, session: Session, limit: int = None, order_by: str = None) -> List[dict]:
def get_all(self, session: Session, limit: int = None, order_by: str = None, override_schema=None) -> List[dict]:
eff_schema = override_schema or self.schema
if self.orm_mode:
return [self.schema.from_orm(x) for x in session.query(self.sql_model).limit(limit).all()]
# list = [x.dict() for x in session.query(self.sql_model).limit(limit).all()]
# if limit == 1:
# return list[0]
# return list
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).limit(limit).all()]
def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]:
"""Queries the database for the selected model. Restricts return responses to the

View file

@ -3,15 +3,35 @@ import json
from fastapi import APIRouter, Depends
from mealie.core.config import APP_VERSION, LOGGER_FILE, app_dirs, settings
from mealie.routes.deps import get_current_user
from mealie.schema.debug import AppInfo
from mealie.schema.debug import AppInfo, DebugInfo
router = APIRouter(prefix="/api/debug", tags=["Debug"])
@router.get("")
async def get_debug_info(current_user=Depends(get_current_user)):
""" Returns general information about the application for debugging """
return DebugInfo(
production=settings.PRODUCTION,
version=APP_VERSION,
demo_status=settings.IS_DEMO,
api_port=settings.API_PORT,
api_docs=settings.API_DOCS,
db_type=settings.DATABASE_TYPE,
sqlite_file=settings.SQLITE_FILE,
default_group=settings.DEFAULT_GROUP,
)
@router.get("/version")
async def get_mealie_version():
""" Returns the current version of mealie"""
return AppInfo(version=APP_VERSION, demo_status=settings.IS_DEMO)
return AppInfo(
version=APP_VERSION,
demo_status=settings.IS_DEMO,
production=settings.PRODUCTION,
)
@router.get("/last-recipe-json")

View file

@ -1,12 +1,14 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user
from mealie.schema.meal import MealPlanIn, MealPlanInDB
from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import GroupInDB, UserInDB
from mealie.services.image import image
from mealie.services.meal_services import get_todays_meal, process_meals
from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@ -74,3 +76,22 @@ def get_today(session: Session = Depends(generate_session), current_user: UserIn
recipe = get_todays_meal(session, group_in_db)
return recipe.slug
@router.get("/today/image", tags=["Meal Plan"])
def get_today(session: Session = Depends(generate_session), group_name: str = "Home"):
"""
Returns the image for todays meal-plan.
"""
group_in_db: GroupInDB = db.groups.get(session, group_name, "name")
recipe = get_todays_meal(session, group_in_db)
if recipe:
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
else:
raise HTTPException(404, "no meal for today")
if recipe_image:
return FileResponse(recipe_image)
else:
raise HTTPException(404, "file not found")

View file

@ -3,13 +3,24 @@ from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.schema.recipe import AllRecipeRequest
from mealie.schema.recipe import AllRecipeRequest, RecipeSummary
from slugify import slugify
from sqlalchemy.orm.session import Session
router = APIRouter(tags=["Query All Recipes"])
@router.get("/api/recipes/summary")
async def get_recipe_summary(
skip=0,
end=9999,
session: Session = Depends(generate_session),
):
""" Returns the summary data for recipes in the database """
return db.recipes.get_all(session, limit=end, override_schema=RecipeSummary)
@router.get("/api/recipes")
def get_all_recipes(
keys: Optional[List[str]] = Query(...),

View file

@ -98,7 +98,6 @@ async def get_recipe_img(recipe_slug: str, image_type: ImageType = ImageType.ori
which_image = IMG_OPTIONS.TINY_IMAGE
recipe_image = read_image(recipe_slug, image_type=which_image)
print(recipe_image)
if recipe_image:
return FileResponse(recipe_image)
else:

View file

@ -1,6 +1,15 @@
from pathlib import Path
from fastapi_camelcase import CamelModel
class AppInfo(CamelModel):
production: bool
version: str
demo_status: bool
class DebugInfo(AppInfo):
api_port: int
api_docs: bool
db_type: str
sqlite_file: Path
default_group: str

View file

@ -34,12 +34,30 @@ class Nutrition(BaseModel):
orm_mode = True
class Recipe(BaseModel):
class RecipeSummary(BaseModel):
name: str
description: Optional[str]
slug: Optional[str] = ""
image: Optional[Any]
recipeYield: Optional[str]
description: Optional[str]
recipeCategory: Optional[List[str]] = []
tags: Optional[List[str]] = []
rating: Optional[int]
class Config:
orm_mode = True
@classmethod
def getter_dict(_cls, name_orm: RecipeModel):
return {
**GetterDict(name_orm),
"recipeCategory": [x.name for x in name_orm.recipeCategory],
"tags": [x.name for x in name_orm.tags],
}
class Recipe(RecipeSummary):
recipeYield: Optional[str]
recipeIngredient: Optional[list[str]]
recipeInstructions: Optional[list[RecipeStep]]
nutrition: Optional[Nutrition]
@ -50,11 +68,8 @@ class Recipe(BaseModel):
performTime: Optional[str] = None
# Mealie Specific
slug: Optional[str] = ""
tags: Optional[List[str]] = []
dateAdded: Optional[datetime.date]
notes: Optional[List[RecipeNote]] = []
rating: Optional[int]
orgURL: Optional[str]
extras: Optional[dict] = {}

148
poetry.lock generated
View file

@ -50,7 +50,7 @@ zookeeper = ["kazoo"]
[[package]]
name = "astroid"
version = "2.5.1"
version = "2.5.2"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
@ -191,11 +191,11 @@ toml = ["toml"]
[[package]]
name = "decorator"
version = "4.4.2"
version = "5.0.4"
description = "Decorators for Humans"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
python-versions = ">=3.5"
[[package]]
name = "ecdsa"
@ -412,11 +412,11 @@ python-versions = "*"
[[package]]
name = "lazy-object-proxy"
version = "1.5.2"
version = "1.6.0"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "livereload"
@ -610,7 +610,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
name = "pillow"
version = "8.2.0"
description = "Python Imaging Library (Fork)"
category = "dev"
category = "main"
optional = false
python-versions = ">=3.6"
@ -698,14 +698,14 @@ python-versions = "*"
[[package]]
name = "pylint"
version = "2.7.2"
version = "2.7.4"
description = "python code static checker"
category = "dev"
optional = false
python-versions = "~=3.6"
[package.dependencies]
astroid = ">=2.5.1,<2.6"
astroid = ">=2.5.2,<2.7"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
isort = ">=4.2.5,<6"
mccabe = ">=0.6,<0.7"
@ -951,7 +951,7 @@ python-versions = ">=3.6"
[[package]]
name = "sqlalchemy"
version = "1.4.2"
version = "1.4.5"
description = "Database Abstraction Library"
category = "main"
optional = false
@ -962,6 +962,7 @@ greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\""}
[package.extras]
aiomysql = ["greenlet (!=0.4.17)", "aiomysql"]
aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"]
asyncio = ["greenlet (!=0.4.17)"]
mariadb_connector = ["mariadb (>=1.0.1)"]
mssql = ["pyodbc"]
@ -977,6 +978,7 @@ postgresql_pg8000 = ["pg8000 (>=1.16.6)"]
postgresql_psycopg2binary = ["psycopg2-binary"]
postgresql_psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql (<1)", "pymysql"]
sqlcipher = ["sqlcipher3-binary"]
[[package]]
name = "starlette"
@ -1162,7 +1164,7 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "32bff6a472fd8564106e2cfa20161a47d271d89ed44a5f2f2483f419fe259c92"
content-hash = "a81463b941cfdbc0e32e215644b172ec1111d5ada27864292d299d7d64fae4cf"
[metadata.files]
aiofiles = [
@ -1182,8 +1184,8 @@ apscheduler = [
{file = "APScheduler-3.7.0.tar.gz", hash = "sha256:1cab7f2521e107d07127b042155b632b7a1cd5e02c34be5a28ff62f77c900c6a"},
]
astroid = [
{file = "astroid-2.5.1-py3-none-any.whl", hash = "sha256:21d735aab248253531bb0f1e1e6d068f0ee23533e18ae8a6171ff892b98297cf"},
{file = "astroid-2.5.1.tar.gz", hash = "sha256:cfc35498ee64017be059ceffab0a25bedf7548ab76f2bea691c5565896e7128d"},
{file = "astroid-2.5.2-py3-none-any.whl", hash = "sha256:cd80bf957c49765dce6d92c43163ff9d2abc43132ce64d4b1b47717c6d2522df"},
{file = "astroid-2.5.2.tar.gz", hash = "sha256:6b0ed1af831570e500e2437625979eaa3b36011f66ddfc4ce930128610258ca9"},
]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
@ -1320,8 +1322,8 @@ coverage = [
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
]
decorator = [
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
{file = "decorator-5.0.4-py3-none-any.whl", hash = "sha256:7280eff5351d7004144b1f302347328c3d06e84271dbe690a5dc4b17eb586994"},
{file = "decorator-5.0.4.tar.gz", hash = "sha256:cdd9d86d8aca11e4496f3cd26d48020db5a2fac247af0e918b3e0bbdb6e4a174"},
]
ecdsa = [
{file = "ecdsa-0.14.1-py2.py3-none-any.whl", hash = "sha256:e108a5fe92c67639abae3260e43561af914e7fd0d27bae6d2ec1312ae7934dfe"},
@ -1444,30 +1446,28 @@ jstyleson = [
{file = "jstyleson-0.0.2.tar.gz", hash = "sha256:680003f3b15a2959e4e6a351f3b858e3c07dd3e073a0d54954e34d8ea5e1308e"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.5.2.tar.gz", hash = "sha256:5944a9b95e97de1980c65f03b79b356f30a43de48682b8bdd90aa5089f0ec1f4"},
{file = "lazy_object_proxy-1.5.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e960e8be509e8d6d618300a6c189555c24efde63e85acaf0b14b2cd1ac743315"},
{file = "lazy_object_proxy-1.5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:522b7c94b524389f4a4094c4bf04c2b02228454ddd17c1a9b2801fac1d754871"},
{file = "lazy_object_proxy-1.5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3782931963dc89e0e9a0ae4348b44762e868ea280e4f8c233b537852a8996ab9"},
{file = "lazy_object_proxy-1.5.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:429c4d1862f3fc37cd56304d880f2eae5bd0da83bdef889f3bd66458aac49128"},
{file = "lazy_object_proxy-1.5.2-cp35-cp35m-win32.whl", hash = "sha256:cd1bdace1a8762534e9a36c073cd54e97d517a17d69a17985961265be6d22847"},
{file = "lazy_object_proxy-1.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:ddbdcd10eb999d7ab292677f588b658372aadb9a52790f82484a37127a390108"},
{file = "lazy_object_proxy-1.5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ecb5dd5990cec6e7f5c9c1124a37cb2c710c6d69b0c1a5c4aa4b35eba0ada068"},
{file = "lazy_object_proxy-1.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b6577f15d5516d7d209c1a8cde23062c0f10625f19e8dc9fb59268859778d7d7"},
{file = "lazy_object_proxy-1.5.2-cp36-cp36m-win32.whl", hash = "sha256:c8fe2d6ff0ff583784039d0255ea7da076efd08507f2be6f68583b0da32e3afb"},
{file = "lazy_object_proxy-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:fa5b2dee0e231fa4ad117be114251bdfe6afe39213bd629d43deb117b6a6c40a"},
{file = "lazy_object_proxy-1.5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1d33d6f789697f401b75ce08e73b1de567b947740f768376631079290118ad39"},
{file = "lazy_object_proxy-1.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:57fb5c5504ddd45ed420b5b6461a78f58cbb0c1b0cbd9cd5a43ad30a4a3ee4d0"},
{file = "lazy_object_proxy-1.5.2-cp37-cp37m-win32.whl", hash = "sha256:e7273c64bccfd9310e9601b8f4511d84730239516bada26a0c9846c9697617ef"},
{file = "lazy_object_proxy-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f4e5e68b7af950ed7fdb594b3f19a0014a3ace0fedb86acb896e140ffb24302"},
{file = "lazy_object_proxy-1.5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cadfa2c2cf54d35d13dc8d231253b7985b97d629ab9ca6e7d672c35539d38163"},
{file = "lazy_object_proxy-1.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e7428977763150b4cf83255625a80a23dfdc94d43be7791ce90799d446b4e26f"},
{file = "lazy_object_proxy-1.5.2-cp38-cp38-win32.whl", hash = "sha256:2f2de8f8ac0be3e40d17730e0600619d35c78c13a099ea91ef7fb4ad944ce694"},
{file = "lazy_object_proxy-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:38c3865bd220bd983fcaa9aa11462619e84a71233bafd9c880f7b1cb753ca7fa"},
{file = "lazy_object_proxy-1.5.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8a44e9901c0555f95ac401377032f6e6af66d8fc1fbfad77a7a8b1a826e0b93c"},
{file = "lazy_object_proxy-1.5.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fa7fb7973c622b9e725bee1db569d2c2ee64d2f9a089201c5e8185d482c7352d"},
{file = "lazy_object_proxy-1.5.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:71a1ef23f22fa8437974b2d60fedb947c99a957ad625f83f43fd3de70f77f458"},
{file = "lazy_object_proxy-1.5.2-cp39-cp39-win32.whl", hash = "sha256:ef3f5e288aa57b73b034ce9c1f1ac753d968f9069cd0742d1d69c698a0167166"},
{file = "lazy_object_proxy-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:37d9c34b96cca6787fe014aeb651217944a967a5b165e2cacb6b858d2997ab84"},
{file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"},
{file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"},
{file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"},
{file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"},
{file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
]
livereload = [
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
@ -1714,8 +1714,8 @@ pyhumps = [
{file = "pyhumps-1.6.1.tar.gz", hash = "sha256:01612603c5ad73a407299d806d30708a3935052276fdd93776953bccc0724e0a"},
]
pylint = [
{file = "pylint-2.7.2-py3-none-any.whl", hash = "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"},
{file = "pylint-2.7.2.tar.gz", hash = "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a"},
{file = "pylint-2.7.4-py3-none-any.whl", hash = "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a"},
{file = "pylint-2.7.4.tar.gz", hash = "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"},
]
pymdown-extensions = [
{file = "pymdown-extensions-8.1.1.tar.gz", hash = "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735"},
@ -1849,40 +1849,40 @@ soupsieve = [
{file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"},
]
sqlalchemy = [
{file = "SQLAlchemy-1.4.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:aed22be55a608787bb6875dbcf3561349a0e88fe33fd88c318c1e5b4eeb2306a"},
{file = "SQLAlchemy-1.4.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7e1b0ed6d720750f02333d2f52502dfc2a23185aacc2cc6ce6ec29d28c21397c"},
{file = "SQLAlchemy-1.4.2-cp27-cp27m-win32.whl", hash = "sha256:9406b96a979ab8d6de5d89f58b1f103c9aeef6fb5367448537a8228619f11258"},
{file = "SQLAlchemy-1.4.2-cp27-cp27m-win_amd64.whl", hash = "sha256:59ec279f1bd55e1d703e3d4b651600cc463cc3eafa8d8e5a70ab844f736348d4"},
{file = "SQLAlchemy-1.4.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8cfcfcf2582b19c874fa20d0b75100abe17be80a4c637c0683b4eb919946dfee"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a6b4b7688fe7d251bbae3f9da4a487568bd584d13201bc7591c8639ad01fecdc"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0abab6d1044198993256f073340b14c459736777c550a7e914cd00444dcf9c30"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:5fb8f6a391992dd6aafe4fdf1dffbf7934fba1f5938593f20b152aa7f9619f82"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:97e333260a99d989f2a131aa8aa74140636dfbd030987150cb3748da607ea7db"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3fa75c854dba3f9b9c28bc5d88d246f6bc6f20b7480367c65339bcb2864d4707"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:1b9f3c7b281aa1c3d0c74ef12c4633e5f8358bb94f01be7b964887183fd53e5e"},
{file = "SQLAlchemy-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:da72e3499bde4548e8b7d7f2ab23ceed09a5bac307bf51057e066c406a0ba2e1"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8383292298bb85d7ad79a13c6571aff213b96c49737f3c3af129de63bbfb42c9"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4d1447183356c9679853926e81c7ebce3fbca9b1c607ea439975298c72137a36"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ff76d7dbf33f62e30e5a1d1b095d46afcdc49e42cbe33ce12014110147466700"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1ba6922331b3f38e116c9266206b044baf64576e5cebd87917b5ad872d7a025f"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:d3b2819f4d7ae56191efc6fc456eb1805ada2bd5ba93d918893bc24fa7a1e30c"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:3b290ff34de625143a05d2d172a88a064bb04a7938265b09d4e4bf45f21948f6"},
{file = "SQLAlchemy-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5289cafee71037f15feeeaf736f01910b9e3572525b73b201bdd21816db010ed"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0bb04fd7414718fb1f4dfa17efcb0be787363451cf99a5e992728925d298d9ae"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4e88549a5e58ba8c80c5ea071ac3b4e590236672a882bb80f56da4afcee45d96"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:edec945ed57d11a1123657e4066f0bf747aaa93c8a65ec1c2c98172d1f2a9b7d"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:06125670280111e39014af87f14d74599fd4b39a512c74f1a10e21e5626eb158"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:e1692bdf1b95c97caab1201773a4576f59627997f598d30bdadc50dd9f897fec"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-win32.whl", hash = "sha256:65c4df9517da9cce2c1255282d3e39f2afbc3a02deba60d99b0a3283ae80ec0b"},
{file = "SQLAlchemy-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6197c88ad53c31f58de5a8180936b8ef027356e788cd5f6514b3439d3d897ac"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:6d6115edf1297bfa58994986ffe0dff21af18f0cba51dfa6d1769aa8a277be32"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:facacaea95e0822f7bbeaa6909b30b2836b14cff8790209d52a0c866e240b673"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6e517126d3bc13d455826befdc35a89f82f01d163848f68db02caa80d25433fc"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:09b08eb1bea621e47c2b0fcb0334fcbb00e1da2a3c2d45a98e56cd072b840719"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:7eba42098a13a3bcd509080b5e44d73783d9129ba0383793979bf518d01e8bb3"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-win32.whl", hash = "sha256:920db115eb06fc507fe2c774fb5c82a898b05dffbdadc7fafad51ce2cfd8c549"},
{file = "SQLAlchemy-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:dcde5067a7dab1ff2eaea2f3622b2055c5225ce2aaf589c5a4c703d43519c4ba"},
{file = "SQLAlchemy-1.4.2.tar.gz", hash = "sha256:6a8e4c2e65028933a6dc8643c8f5a4f295a367131195b3c708634925cb3e8ec1"},
{file = "SQLAlchemy-1.4.5-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c3810ebcf1d42c532c8f5c3f442c705d94442a27a32f2df5344f0857306ab321"},
{file = "SQLAlchemy-1.4.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7481f9c2c832a3bf37c80bee44d91ac9938b815cc06f7e795b976e300914aab9"},
{file = "SQLAlchemy-1.4.5-cp27-cp27m-win32.whl", hash = "sha256:94040a92b6676f9ffdab6c6b479b3554b927a635c90698c761960b266b04fc88"},
{file = "SQLAlchemy-1.4.5-cp27-cp27m-win_amd64.whl", hash = "sha256:02b039e0e7e6de2f15ea2d2de3995e31a170e700ec0b37b4eded662171711d19"},
{file = "SQLAlchemy-1.4.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f16801795f1ffe9472360589a04301018c79e4582a85e68067275bb4f765e4e2"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:82f11b679df91275788be6734dd4a9dfa29bac67b85326992609f62b05bdab37"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a08027ae84efc563f0f2f341dda572eadebeca38c0ae028a009988f27e9e6230"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:70a1387396ea5b3022539b560c287daf79403d8b4b365f89b56d660e625a4457"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4f7ce3bfdab6520554af4a5b1df4513d45388624d015ba4d921daf48ce1d6503"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:08943201a1e3c6238e48f4d5d56c27ea1e1b39d3d9f36a9d81fc3cfb0e1b83bd"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-win32.whl", hash = "sha256:fbb0fda1c574975807aceb0e2332e0ecfe9e5656c191ed482c1a5eafe7a33823"},
{file = "SQLAlchemy-1.4.5-cp36-cp36m-win_amd64.whl", hash = "sha256:8d6a9feb5efd2fdab25c6d5a0a5589fed9d789f5ec57ec12263fd0e60ce1dea6"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:c22bfac8d3b955cdb13f0fcd6343156bf56d925196cf7d9ab9ce9f61d3f1e11c"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7c0c7bb49167ac738ca6ee6e7f94a9988a7e4e261d8da335341e8c8c8f3b2e9b"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:344b58b4b4193b72e8b768a51ef6eb5a4c948ce313a0f23e2ea081e71ce8ac0e"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:48540072f43b3c080159ec1f24a4b014c0ee83d3b73795399974aa358a8cf71b"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:81badd7d3e0e6aba70a5d1b50fabe8112e9835a6fdb0684054c3fe5378ce0d01"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:a103294583383660d9e06dbd82037dc8e94c184bdcb27b2be44ae4457dafc6b4"},
{file = "SQLAlchemy-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5361e25181b9872d6906c8c9be7dc05cb0a0951d71ee59ee5a71c1deb301b8a8"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:7f5087104c3c5af11ea59e49ae66c33ca98b14a47d3796ae97498fca53f84aef"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:11e7a86209f69273e75d2dd64b06c0c2660e39cd942fce2170515c404ed7358a"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8301ecf3e819eb5dbc171e84654ff60872807775301a55fe35b0ab2ba3742031"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:44e11a06168782b6d485daef197783366ce7ab0d5eea0066c899ae06cef47bbc"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6f8fdad2f335d2f3ca2f3ee3b01404f7abcf519b03de2c510f1f42d16e39ffb4"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-win32.whl", hash = "sha256:f62c57ceadedeb8e7b98b48ac4d684bf2b0f73b9d882fed3ca260d9aedf6403f"},
{file = "SQLAlchemy-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:301d0cd6ef1dc73b607748183da857e712d6f743de8d92b1e1f8facfb0ba2aa2"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:915d4fa08776c0252dc5a34fa15c6490f66f411ea1ac9492022f98875d6baf20"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7de84feb31af3d8fdf819cac2042928d0b60d3cb16f49c4b2f48d88db46e79f6"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:45b091ccbf94374ed14abde17e9a04522b0493a17282eaaf4383efdd413f5243"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4df07161897191ed8d4a0cfc92425c81296160e5c5f76c9256716d3085172883"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ee4ddc904fb6414b5118af5b8d45e428aac2ccda01326b2ba2fe4354b0d8d1ae"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-win32.whl", hash = "sha256:2f11b5783933bff55291ca06496124347627d211ff2e509e846af1c35de0a3fb"},
{file = "SQLAlchemy-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:0ee0054d4a598d2920cae14bcbd33e200e02c5e3b47b902627f8cf5d4c9a2a4b"},
{file = "SQLAlchemy-1.4.5.tar.gz", hash = "sha256:1294f05916c044631fd626a4866326bbfbd17f62bd37510d000afaef4b35bd74"},
]
starlette = [
{file = "starlette-0.13.6-py3-none-any.whl", hash = "sha256:bd2ffe5e37fb75d014728511f8e68ebf2c80b0fa3d04ca1479f4dc752ae31ac9"},

View file

@ -30,6 +30,8 @@ bcrypt = "^3.2.0"
python-jose = "^3.2.0"
passlib = "^1.7.4"
lxml = "4.6.2"
Pillow = "^8.2.0"
[tool.poetry.dev-dependencies]
pylint = "^2.6.0"
@ -39,7 +41,6 @@ pytest-cov = "^2.11.0"
mkdocs-material = "^7.0.2"
flake8 = "^3.9.0"
coverage = "^5.5"
Pillow = "^8.2.0"
[build-system]
requires = ["poetry-core>=1.0.0"]