Add alembic

This commit is contained in:
Antoine Bertin 2021-04-28 00:32:53 +02:00
commit 3022cc20ec
No known key found for this signature in database
GPG key ID: 09851B52754E2327
19 changed files with 2712 additions and 2227 deletions

89
alembic.ini Normal file
View file

@ -0,0 +1,89 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = sqlite:///mealie.db
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks=black
# black.type=console_scripts
# black.entrypoint=black
# black.options=-l 79
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

1
alembic/README Normal file
View file

@ -0,0 +1 @@
Generic single-database configuration.

79
alembic/env.py Normal file
View file

@ -0,0 +1,79 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from mealie.db.models.model_base import SqlAlchemyBase as Base
import mealie.db.models._all_models
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

24
alembic/script.py.mako Normal file
View file

@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View file

@ -0,0 +1,318 @@
"""Initial revision
Revision ID: 89e8733c36f2
Revises:
Create Date: 2021-04-27 23:37:22.500465
"""
from mealie.core.config import settings
from mealie.core.security import get_password_hash
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '89e8733c36f2'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
categories_table = op.create_table('categories',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('slug', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_categories_name'), 'categories', ['name'], unique=False)
op.create_index(op.f('ix_categories_slug'), 'categories', ['slug'], unique=True)
custom_pages_table = op.create_table('custom_pages',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('position', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('slug', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
groups_table = op.create_table('groups',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('webhook_enable', sa.Boolean(), nullable=True),
sa.Column('webhook_time', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_groups_name'), 'groups', ['name'], unique=True)
recipes_table = op.create_table('recipes',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('description', sa.String(), nullable=True),
sa.Column('image', sa.String(), nullable=True),
sa.Column('totalTime', sa.String(), nullable=True),
sa.Column('prepTime', sa.String(), nullable=True),
sa.Column('performTime', sa.String(), nullable=True),
sa.Column('cookTime', sa.String(), nullable=True),
sa.Column('recipeYield', sa.String(), nullable=True),
sa.Column('recipeCuisine', sa.String(), nullable=True),
sa.Column('slug', sa.String(), nullable=True),
sa.Column('dateAdded', sa.Date(), nullable=True),
sa.Column('rating', sa.Integer(), nullable=True),
sa.Column('orgURL', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_recipes_slug'), 'recipes', ['slug'], unique=True)
sign_ups_table = op.create_table('sign_ups',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('token', sa.String(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('admin', sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_sign_ups_name'), 'sign_ups', ['name'], unique=False)
op.create_index(op.f('ix_sign_ups_token'), 'sign_ups', ['token'], unique=False)
site_settings_table = op.create_table('site_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('language', sa.String(), nullable=True),
sa.Column('show_recent', sa.Boolean(), nullable=True),
sa.Column('cards_per_section', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
site_theme_table = op.create_table('site_theme',
sa.Column('name', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('name')
)
tags_table = op.create_table('tags',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('slug', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_tags_name'), 'tags', ['name'], unique=False)
op.create_index(op.f('ix_tags_slug'), 'tags', ['slug'], unique=True)
api_extras_table = op.create_table('api_extras',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('key_name', sa.String(), nullable=True),
sa.Column('value', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('key_name')
)
custom_pages2categories_table = op.create_table('custom_pages2categories',
sa.Column('custom_page_id', sa.Integer(), nullable=True),
sa.Column('category_slug', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
sa.ForeignKeyConstraint(['custom_page_id'], ['custom_pages.id'], )
)
group2categories_table = op.create_table('group2categories',
sa.Column('group_id', sa.Integer(), nullable=True),
sa.Column('category_slug', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], )
)
mealplan_table = op.create_table('mealplan',
sa.Column('uid', sa.Integer(), nullable=False),
sa.Column('startDate', sa.Date(), nullable=True),
sa.Column('endDate', sa.Date(), nullable=True),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('uid'),
sa.UniqueConstraint('uid')
)
notes_table = op.create_table('notes',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('title', sa.String(), nullable=True),
sa.Column('text', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id')
)
recipe_instructions_table = op.create_table('recipe_instructions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('position', sa.Integer(), nullable=True),
sa.Column('type', sa.String(), nullable=True),
sa.Column('text', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id')
)
recipe_nutrition_table = op.create_table('recipe_nutrition',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('calories', sa.String(), nullable=True),
sa.Column('fatContent', sa.String(), nullable=True),
sa.Column('fiberContent', sa.String(), nullable=True),
sa.Column('proteinContent', sa.String(), nullable=True),
sa.Column('sodiumContent', sa.String(), nullable=True),
sa.Column('sugarContent', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id')
)
recipes2categories_table = op.create_table('recipes2categories',
sa.Column('recipe_id', sa.Integer(), nullable=True),
sa.Column('category_slug', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
sa.ForeignKeyConstraint(['recipe_id'], ['recipes.id'], )
)
recipes2tags_table = op.create_table('recipes2tags',
sa.Column('recipe_id', sa.Integer(), nullable=True),
sa.Column('tag_slug', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['recipe_id'], ['recipes.id'], ),
sa.ForeignKeyConstraint(['tag_slug'], ['tags.slug'], )
)
recipes_ingredients_table = op.create_table('recipes_ingredients',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('position', sa.Integer(), nullable=True),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('ingredient', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id')
)
site_settings2categories_table = op.create_table('site_settings2categories_table',
sa.Column('sidebar_id', sa.Integer(), nullable=True),
sa.Column('category_slug', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['category_slug'], ['categories.slug'], ),
sa.ForeignKeyConstraint(['sidebar_id'], ['site_settings.id'], )
)
theme_colors_table = op.create_table('theme_colors',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.String(), nullable=True),
sa.Column('primary', sa.String(), nullable=True),
sa.Column('accent', sa.String(), nullable=True),
sa.Column('secondary', sa.String(), nullable=True),
sa.Column('success', sa.String(), nullable=True),
sa.Column('info', sa.String(), nullable=True),
sa.Column('warning', sa.String(), nullable=True),
sa.Column('error', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['site_theme.name'], ),
sa.PrimaryKeyConstraint('id')
)
tools_table = op.create_table('tools',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('tool', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['recipes.id'], ),
sa.PrimaryKeyConstraint('id')
)
users_table = op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('full_name', sa.String(), nullable=True),
sa.Column('email', sa.String(), nullable=True),
sa.Column('password', sa.String(), nullable=True),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.Column('admin', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.create_index(op.f('ix_users_full_name'), 'users', ['full_name'], unique=False)
webhook_urls_table = op.create_table('webhook_urls',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('url', sa.String(), nullable=True),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
meal_table = op.create_table('meal',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('parent_id', sa.Integer(), nullable=True),
sa.Column('slug', sa.String(), nullable=True),
sa.Column('name', sa.String(), nullable=True),
sa.Column('date', sa.Date(), nullable=True),
sa.Column('image', sa.String(), nullable=True),
sa.Column('description', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['parent_id'], ['mealplan.uid'], ),
sa.PrimaryKeyConstraint('id')
)
# seed data
op.bulk_insert(site_settings_table, [
{
"id": 1,
"language": "en",
"firstDayOfWeek": 0,
"showRecent": True,
}
])
op.bulk_insert(categories_table, [
{"id": 1, "name": "thanksgiving", "slug": "thanksgiving"},
{"id": 2, "name": "homechef", "slug": "homechef"},
{"id": 3, "name": "potatoes", "slug": "potatoes"},
])
op.bulk_insert(site_settings2categories_table, [
{
"sidebar_id": 1,
"category_slug", "thanksgiving",
},
{
"sidebar_id": 1,
"category_slug", "homechef",
},
{
"sidebar_id": 1,
"category_slug", "potatoes",
},
])
op.bulk_insert(site_theme_table, [
{
"name": "default",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
},
])
op.bulk_insert(groups_table, [
{
"name": settings.DEFAULT_GROUP,
},
])
op.bulk_insert(users_table, [
{
"full_name": "Change Me",
"email": settings.DEFAULT_EMAIL,
"password": get_password_hash(settings.DEFAULT_PASSWORD),
"group": settings.DEFAULT_GROUP,
"admin": True,
},
])
def downgrade():
op.drop_table('meal')
op.drop_table('webhook_urls')
op.drop_index(op.f('ix_users_full_name'), table_name='users')
op.drop_index(op.f('ix_users_email'), table_name='users')
op.drop_table('users')
op.drop_table('tools')
op.drop_table('theme_colors')
op.drop_table('site_settings2categories')
op.drop_table('recipes_ingredients')
op.drop_table('recipes2tags')
op.drop_table('recipes2categories')
op.drop_table('recipe_nutrition')
op.drop_table('recipe_instructions')
op.drop_table('notes')
op.drop_table('mealplan')
op.drop_table('group2categories')
op.drop_table('custom_pages2categories')
op.drop_table('api_extras')
op.drop_index(op.f('ix_tags_slug'), table_name='tags')
op.drop_index(op.f('ix_tags_name'), table_name='tags')
op.drop_table('tags')
op.drop_table('site_theme')
op.drop_table('site_settings')
op.drop_index(op.f('ix_sign_ups_token'), table_name='sign_ups')
op.drop_index(op.f('ix_sign_ups_name'), table_name='sign_ups')
op.drop_table('sign_ups')
op.drop_index(op.f('ix_recipes_slug'), table_name='recipes')
op.drop_table('recipes')
op.drop_index(op.f('ix_groups_name'), table_name='groups')
op.drop_table('groups')
op.drop_table('custom_pages')
op.drop_index(op.f('ix_categories_slug'), table_name='categories')
op.drop_index(op.f('ix_categories_name'), table_name='categories')
op.drop_table('categories')

View file

@ -123,7 +123,7 @@ The frontend static files are generated with `npm run build`. This is done durin
### Backend API
The backend API is build with Python, FastAPI, and SQLite and requires Python 3.9, and Poetry. Once the requirements are installed, in the project directory you can run the command `poetry install` to create a python virtual environment and install the python dependencies.
Once the dependencies are installed you should be ready to run the server. To initialize that database you need to first run `python mealie/db/init_db.py`. Then to start The web server, you run the command `uvicorn mealie.app:app --host 0.0.0.0 --port 9000`
Once the dependencies are installed you should be ready to run the server. To initialize that database you need to first run `alembic upgrade head`. Then to start The web server, you run the command `uvicorn mealie.app:app --host 0.0.0.0 --port 9000`
### Proxy Server

View file

@ -53,7 +53,7 @@ setup: ## Setup Development Instance
cd ..
backend: ## Start Mealie Backend Development Server
poetry run python mealie/db/init_db.py && \
poetry run alembic upgrade head && \
poetry run python mealie/services/image/minify.py && \
poetry run python mealie/app.py

View file

@ -3,8 +3,6 @@ from sqlalchemy.orm.session import Session
from mealie.db.models.db_session import sql_global_init
sql_exists = True
SessionLocal = sql_global_init(settings.DB_URL)

View file

@ -1,62 +0,0 @@
from mealie.core import root_logger
from mealie.core.config import settings
from mealie.core.security import get_password_hash
from mealie.db.database import db
from mealie.db.db_setup import create_session, sql_exists
from mealie.schema.settings import SiteSettings
from mealie.schema.theme import SiteTheme
from sqlalchemy.orm import Session
logger = root_logger.get_logger("init_db")
def init_db(db: Session = None) -> None:
if not db:
db = create_session()
default_group_init(db)
default_settings_init(db)
default_theme_init(db)
default_user_init(db)
db.close()
def default_theme_init(session: Session):
db.themes.create(session, SiteTheme().dict())
def default_settings_init(session: Session):
document = db.settings.create(session, SiteSettings().dict())
logger.info(f"Created Site Settings: \n {document}")
def default_group_init(session: Session):
default_group = {"name": settings.DEFAULT_GROUP}
logger.info("Generating Default Group")
db.groups.create(session, default_group)
def default_user_init(session: Session):
default_user = {
"full_name": "Change Me",
"email": settings.DEFAULT_EMAIL,
"password": get_password_hash(settings.DEFAULT_PASSWORD),
"group": settings.DEFAULT_GROUP,
"admin": True,
}
logger.info("Generating Default User")
db.users.create(session, default_user)
def main():
if sql_exists:
print("Database Exists")
else:
print("Database Doesn't Exists, Initializing...")
init_db()
if __name__ == "__main__":
main()

View file

@ -13,8 +13,4 @@ def sql_global_init(db_url: str):
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
import mealie.db.models._all_models # noqa: F401
SqlAlchemyBase.metadata.create_all(engine)
return SessionLocal

View file

@ -8,7 +8,7 @@ from sqlalchemy.orm import validates
logger = root_logger.get_logger()
site_settings2categories = sa.Table(
"site_settings2categoories",
"site_settings2categories",
SqlAlchemyBase.metadata,
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),

View file

@ -11,11 +11,7 @@ recipes2tags = sa.Table(
"recipes2tags",
SqlAlchemyBase.metadata,
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
<<<<<<< HEAD
sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")),
=======
sa.Column("tag_slug", sa.String, sa.ForeignKey("tags.slug")),
>>>>>>> e2e2ad5 (feat: other databases support)
)

BIN
mealie/mealie.db Normal file

Binary file not shown.

View file

@ -7,7 +7,7 @@ ARG1=${1:-production}
# DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# # Initialize Database Prerun
poetry run python /app/mealie/db/init_db.py
poetry run alembic upgrade head
poetry run python /app/mealie/services/image/minify.py
# Migrations

View file

@ -16,6 +16,7 @@ class SiteSettings(CamelModel):
class Config:
orm_mode = True
<<<<<<< HEAD
schema_extra = {
"example": {
"language": "en",
@ -29,6 +30,8 @@ class SiteSettings(CamelModel):
}
}
=======
>>>>>>> 0afdaf6 (Add alembic)
class CustomPageBase(CamelModel):
name: str

View file

@ -20,17 +20,3 @@ class SiteTheme(BaseModel):
class Config:
orm_mode = True
schema_extra = {
"example": {
"name": "default",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
}
}

76
poetry.lock generated
View file

@ -6,6 +6,20 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "alembic"
version = "1.5.8"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
[package.dependencies]
Mako = "*"
python-dateutil = "*"
python-editor = ">=0.3"
SQLAlchemy = ">=1.3.0"
[[package]]
name = "aniso8601"
version = "7.0.0"
@ -460,6 +474,21 @@ html5 = ["html5lib"]
htmlsoup = ["beautifulsoup4"]
source = ["Cython (>=0.29.7)"]
[[package]]
name = "mako"
version = "1.1.4"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
MarkupSafe = ">=0.9.2"
[package.extras]
babel = ["babel"]
lingua = ["lingua"]
[[package]]
name = "markdown"
version = "3.3.4"
@ -792,6 +821,17 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"]
[[package]]
name = "python-dateutil"
version = "2.8.1"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
[package.dependencies]
six = ">=1.5"
[[package]]
name = "python-dotenv"
version = "0.15.0"
@ -803,6 +843,14 @@ python-versions = "*"
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
name = "python-editor"
version = "1.0.4"
description = "Programmatically open an editor, capture the result."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "python-jose"
version = "3.2.0"
@ -1175,13 +1223,17 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "bfdb4d3d5d69e53f16b315f993b712a703058d3f59e24644681ccc9062cf5143"
content-hash = "2e18cdb78fe35d17c6018a71ed28cdb545f73fa60030a999a3c90bb9a9a1b245"
[metadata.files]
aiofiles = [
{file = "aiofiles-0.5.0-py3-none-any.whl", hash = "sha256:377fdf7815cc611870c59cbd07b68b180841d2a2b79812d8c218be02448c2acb"},
{file = "aiofiles-0.5.0.tar.gz", hash = "sha256:98e6bcfd1b50f97db4980e182ddd509b7cc35909e903a8fe50d8849e02d815af"},
]
alembic = [
{file = "alembic-1.5.8-py2.py3-none-any.whl", hash = "sha256:8a259f0a4c8b350b03579d77ce9e810b19c65bf0af05f84efb69af13ad50801e"},
{file = "alembic-1.5.8.tar.gz", hash = "sha256:e27fd67732c97a1c370c33169ef4578cf96436fa0e7dcfaeeef4a917d0737d56"},
]
aniso8601 = [
{file = "aniso8601-7.0.0-py2.py3-none-any.whl", hash = "sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b"},
{file = "aniso8601-7.0.0.tar.gz", hash = "sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e"},
@ -1526,6 +1578,9 @@ lxml = [
{file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf"},
{file = "lxml-4.6.2.tar.gz", hash = "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc"},
]
mako = [
{file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"},
]
markdown = [
{file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"},
{file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"},
@ -1752,10 +1807,21 @@ pytest-cov = [
{file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
{file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
]
python-dateutil = [
{file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
{file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
]
python-dotenv = [
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
]
python-editor = [
{file = "python-editor-1.0.4.tar.gz", hash = "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b"},
{file = "python_editor-1.0.4-py2-none-any.whl", hash = "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"},
{file = "python_editor-1.0.4-py2.7.egg", hash = "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"},
{file = "python_editor-1.0.4-py3-none-any.whl", hash = "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d"},
{file = "python_editor-1.0.4-py3.5.egg", hash = "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77"},
]
python-jose = [
{file = "python-jose-3.2.0.tar.gz", hash = "sha256:4e4192402e100b5fb09de5a8ea6bcc39c36ad4526341c123d401e2561720335b"},
{file = "python_jose-3.2.0-py2.py3-none-any.whl", hash = "sha256:67d7dfff599df676b04a996520d9be90d6cdb7e6dd10b4c7cacc0c3e2e92f2be"},
@ -1777,26 +1843,18 @@ pyyaml = [
{file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
{file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
{file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
{file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
{file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
{file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
{file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
{file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
{file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
{file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
{file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
{file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
{file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
{file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
{file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
{file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
{file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
{file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
{file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
{file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
{file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
{file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
{file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
{file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
{file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},

View file

@ -32,6 +32,7 @@ passlib = "^1.7.4"
lxml = "4.6.2"
Pillow = "^8.2.0"
pathvalidate = "^2.4.1"
alembic = "^1.5.8"
[tool.poetry.dev-dependencies]

View file

@ -5,7 +5,6 @@ from fastapi.testclient import TestClient
from mealie.app import app
from mealie.core.config import app_dirs, settings
from mealie.db.db_setup import generate_session, sql_global_init
from mealie.db.init_db import init_db
from pytest import fixture
from tests.app_routes import AppRoutes
@ -17,7 +16,6 @@ SQLITE_FILE.unlink(missing_ok=True)
TestSessionLocal = sql_global_init(SQLITE_FILE)
init_db(TestSessionLocal())
def override_get_db():