diff --git a/.dockerignore b/.dockerignore index 49ef68257..f88a2a24b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,7 +6,7 @@ .idea .vscode -__pycache__/ +**/__pycache__/ *.py[cod] *$py.class *.so @@ -25,9 +25,11 @@ venv */node_modules */dist +/dist/ */data/db */mealie/test */mealie/.temp +/mealie/frontend/ model.crfmodel diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 48d020811..df3e5b3a2 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -3,8 +3,15 @@ on: workflow_call: jobs: + build-package: + name: "Build Python package" + uses: ./.github/workflows/partial-package.yml + with: + tag: e2e + test: timeout-minutes: 60 + needs: build-package runs-on: ubuntu-latest defaults: run: @@ -18,11 +25,18 @@ jobs: cache-dependency-path: ./tests/e2e/yarn.lock - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Retrieve Python package + uses: actions/download-artifact@v4 + with: + name: backend-dist + path: dist - name: Build Image uses: docker/build-push-action@v5 with: file: ./docker/Dockerfile context: . + build-contexts: | + packages=dist push: false load: true tags: mealie:e2e diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index fdf1c00c2..e2558c4bc 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -21,7 +21,7 @@ jobs: uses: ./.github/workflows/partial-backend.yml frontend-tests: - name: "Frontend and End-to-End Tests" + name: "Frontend Tests" uses: ./.github/workflows/partial-frontend.yml build-release: diff --git a/.github/workflows/partial-backend.yml b/.github/workflows/partial-backend.yml index b0772d181..89d809d16 100644 --- a/.github/workflows/partial-backend.yml +++ b/.github/workflows/partial-backend.yml @@ -1,4 +1,4 @@ -name: Backend Test/Lint +name: Backend Lint and Test on: workflow_call: diff --git a/.github/workflows/partial-builder.yml b/.github/workflows/partial-builder.yml index c6362ba3b..573325da1 100644 --- a/.github/workflows/partial-builder.yml +++ b/.github/workflows/partial-builder.yml @@ -16,7 +16,14 @@ on: required: true jobs: + build-package: + name: "Build Python package" + uses: ./.github/workflows/partial-package.yml + with: + tag: ${{ inputs.tag }} + publish: + needs: build-package runs-on: ubuntu-latest steps: - name: Checkout repository @@ -35,18 +42,22 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Override __init__.py - run: | - echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py - - uses: depot/setup-action@v1 + - name: Retrieve Python package + uses: actions/download-artifact@v4 + with: + name: backend-dist + path: dist + - name: Build and push Docker image, via Depot.dev uses: depot/build-push-action@v1 with: project: srzjb6mhzm file: ./docker/Dockerfile context: . + build-contexts: | + packages=dist platforms: linux/amd64,linux/arm64 push: true tags: | diff --git a/.github/workflows/partial-frontend.yml b/.github/workflows/partial-frontend.yml index bbebe4cca..00f8a2673 100644 --- a/.github/workflows/partial-frontend.yml +++ b/.github/workflows/partial-frontend.yml @@ -1,4 +1,4 @@ -name: Frontend Build/Lin +name: Frontend Lint and Test on: workflow_call: @@ -41,37 +41,3 @@ jobs: - name: Run tests ๐Ÿงช run: yarn test:ci working-directory: "frontend" - - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout ๐Ÿ›Ž - uses: actions/checkout@v4 - - - name: Setup node env ๐Ÿ— - uses: actions/setup-node@v4.0.0 - with: - node-version: 16 - check-latest: true - - - name: Get yarn cache directory path ๐Ÿ›  - id: yarn-cache-dir-path - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - - name: Cache node_modules ๐Ÿ“ฆ - uses: actions/cache@v4 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Install dependencies ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป - run: yarn - working-directory: "frontend" - - - name: Run Build ๐Ÿšš - run: yarn build - working-directory: "frontend" diff --git a/.github/workflows/partial-package.yml b/.github/workflows/partial-package.yml new file mode 100644 index 000000000..1ee258562 --- /dev/null +++ b/.github/workflows/partial-package.yml @@ -0,0 +1,102 @@ +name: Package build + +on: + workflow_call: + inputs: + tag: + required: true + type: string + +jobs: + build-frontend: + name: Build frontend + runs-on: ubuntu-latest + + steps: + - name: Checkout ๐Ÿ›Ž + uses: actions/checkout@v4 + + - name: Setup node env ๐Ÿ— + uses: actions/setup-node@v4.0.0 + with: + node-version: 16 + check-latest: true + + - name: Get yarn cache directory path ๐Ÿ›  + id: yarn-cache-dir-path + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + + - name: Cache node_modules ๐Ÿ“ฆ + uses: actions/cache@v4 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install dependencies ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป + run: yarn + working-directory: "frontend" + + - name: Run Build ๐Ÿšš + run: yarn generate + working-directory: "frontend" + + - name: Archive built frontend + uses: actions/upload-artifact@v4 + with: + name: frontend-dist + path: frontend/dist + retention-days: 5 + + build-package: + name: Build Python package + needs: build-frontend + runs-on: ubuntu-latest + + steps: + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + plugins: | + poetry-plugin-export + + - name: Retrieve built frontend + uses: actions/download-artifact@v4 + with: + name: frontend-dist + path: mealie/frontend + + - name: Override __init__.py + run: | + echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py + + - name: Build package and requirements.txt + env: + SKIP_PACKAGE_DEPS: true + run: | + task py:package + + - name: Archive built package + uses: actions/upload-artifact@v4 + with: + name: backend-dist + path: dist + retention-days: 5 diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index ad2fa13e3..1cddb2d52 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -19,7 +19,7 @@ jobs: uses: ./.github/workflows/partial-backend.yml frontend-tests: - name: "Frontend and End-to-End Tests" + name: "Frontend Tests" uses: ./.github/workflows/partial-frontend.yml container-scanning: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9fbb398cc..55b0ec5d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: uses: ./.github/workflows/partial-backend.yml frontend-tests: - name: "Frontend and End-to-End Tests" + name: "Frontend Tests" uses: ./.github/workflows/partial-frontend.yml build-release: diff --git a/.gitignore b/.gitignore index f853ecb97..3dbf0a6bd 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ pnpm-debug.log* env/ build/ develop-eggs/ - +/dist/ downloads/ eggs/ .eggs/ @@ -66,6 +66,9 @@ wheels/ .installed.cfg *.egg +# frontend copied into Python module for packaging purposes +/mealie/frontend/ + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a36797a8..1dfd0a074 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: exclude: ^tests/data/ - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.9.4 + rev: v0.9.7 hooks: - id: ruff - id: ruff-format diff --git a/.vscode/settings.json b/.vscode/settings.json index e4bfdeb9d..b842cd45e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -60,5 +60,9 @@ }, "[vue]": { "editor.formatOnSave": false + }, + "[python]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "charliermarsh.ruff" } } diff --git a/Taskfile.yml b/Taskfile.yml index 70f6f23dd..8c017a286 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -41,14 +41,25 @@ tasks: setup:ui: desc: setup frontend dependencies dir: frontend + run: once cmds: - yarn install + sources: + - package.json + - yarn.lock + generates: + - node_modules/** setup:py: desc: setup python dependencies + run: once cmds: - poetry install --with main,dev,postgres - poetry run pre-commit install + sources: + - poetry.lock + - pyproject.toml + - .pre-commit-config.yaml setup:model: desc: setup nlp model @@ -131,6 +142,63 @@ tasks: - poetry run coverage html - open htmlcov/index.html + py:package:copy-frontend: + desc: copy the frontend files into the Python package + internal: true + deps: + - ui:generate + cmds: + - rm -rf mealie/frontend + - cp -a frontend/dist mealie/frontend + sources: + - frontend/dist/** + generates: + - mealie/frontend/** + + py:package:generate-requirements: + desc: Generate requirements file to pin all packages, effectively a "pip freeze" before installation begins + internal: true + cmds: + - poetry export -n --only=main --extras=pgsql --output=dist/requirements.txt + # Include mealie in the requirements, hashing the package that was just built to ensure it's the one installed + - echo "mealie[pgsql]=={{.MEALIE_VERSION}} \\" >> dist/requirements.txt + - poetry run pip hash dist/mealie-{{.MEALIE_VERSION}}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt + - echo " \\" >> dist/requirements.txt + - poetry run pip hash dist/mealie-{{.MEALIE_VERSION}}.tar.gz | tail -n1 >> dist/requirements.txt + vars: + MEALIE_VERSION: + sh: poetry version --short + sources: + - poetry.lock + - pyproject.toml + - dist/mealie-*.whl + - dist/mealie-*.tar.gz + generates: + - dist/requirements.txt + + py:package:deps-parallel: + desc: Run py:package dependencies in parallel + internal: true + deps: + - setup:py + - py:package:copy-frontend + + py:package:deps: + desc: Dependencies of py:package, skippable by setting SKIP_PACKAGE_DEPS=true + internal: true + cmds: + - task: py:package:deps-parallel + status: + - '{{ .SKIP_PACKAGE_DEPS | default "false"}}' + + py:package: + desc: builds Python packages (sdist and wheel) in top-level dist directory + deps: + - py:package:deps + cmds: + - poetry build -n --output=dist + - task: py:package:generate-requirements + py: desc: runs the backend server cmds: @@ -160,6 +228,14 @@ tasks: cmds: - yarn build + ui:generate: + desc: generates a static version of the frontend in frontend/dist + dir: frontend + deps: + - setup:ui + cmds: + - yarn generate + ui:lint: desc: runs the frontend linter dir: frontend @@ -184,6 +260,16 @@ tasks: cmds: - yarn run dev + docker:build-from-package: + desc: Builds the Docker image from the existing Python package in dist/ + deps: + - py:package + cmds: + - docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT={{.GIT_COMMIT}} --build-context packages=dist . + vars: + GIT_COMMIT: + sh: git rev-parse HEAD + docker:prod: desc: builds and runs the production docker image locally dir: docker diff --git a/docker/Dockerfile b/docker/Dockerfile index bdee7416e..f08261129 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,11 @@ -FROM node:16 as builder +############################################### +# Frontend Build +############################################### +FROM node:16 AS frontend-builder -WORKDIR /app +WORKDIR /frontend -COPY ./frontend . +COPY frontend . RUN yarn install \ --prefer-offline \ @@ -26,14 +29,10 @@ ENV PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=off \ PIP_DISABLE_PIP_VERSION_CHECK=on \ PIP_DEFAULT_TIMEOUT=100 \ - POETRY_HOME="/opt/poetry" \ - POETRY_VIRTUALENVS_IN_PROJECT=true \ - POETRY_NO_INTERACTION=1 \ - PYSETUP_PATH="/opt/pysetup" \ - VENV_PATH="/opt/pysetup/.venv" + VENV_PATH="/opt/mealie" -# prepend poetry and venv to path -ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" +# prepend venv to path +ENV PATH="$VENV_PATH/bin:$PATH" # create user account RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \ @@ -41,31 +40,81 @@ RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \ && mkdir $MEALIE_HOME ############################################### -# Builder Image +# Backend Package Build ############################################### -FROM python-base as builder-base +FROM python-base AS backend-builder RUN apt-get update \ && apt-get install --no-install-recommends -y \ curl \ + && rm -rf /var/lib/apt/lists/* + +ENV POETRY_HOME="/opt/poetry" \ + POETRY_NO_INTERACTION=1 + +# prepend poetry to path +ENV PATH="$POETRY_HOME/bin:$PATH" + +# install poetry - respects $POETRY_VERSION & $POETRY_HOME +ENV POETRY_VERSION=2.0.1 +RUN curl -sSL https://install.python-poetry.org | python3 - + +# install poetry plugins needed to build the package +RUN poetry self add "poetry-plugin-export>=1.9" + +WORKDIR /mealie + +# copy project files here to ensure they will be cached. +COPY poetry.lock pyproject.toml ./ +COPY mealie ./mealie + +# Copy frontend to package it into the wheel +COPY --from=frontend-builder /frontend/dist ./mealie/frontend + +# Build the source and binary package +RUN poetry build --output=dist + +# Create the requirements file, which is used to install the built package and +# its pinned dependencies later. mealie is included to ensure the built one is +# what's installed. +RUN export MEALIE_VERSION=$(poetry version --short) \ + && poetry export --only=main --extras=pgsql --output=dist/requirements.txt \ + && echo "mealie[pgsql]==$MEALIE_VERSION \\" >> dist/requirements.txt \ + && poetry run pip hash dist/mealie-$MEALIE_VERSION-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt \ + && echo " \\" >> dist/requirements.txt \ + && poetry run pip hash dist/mealie-$MEALIE_VERSION.tar.gz | tail -n1 >> dist/requirements.txt + +############################################### +# Package Container +# Only role is to hold the packages, or be overriden by a --build-context flag. +############################################### +FROM scratch AS packages +COPY --from=backend-builder /mealie/dist / + +############################################### +# Python Virtual Environment Build +############################################### +# Install packages required to build the venv, in parallel to building the wheel +FROM python-base AS venv-builder-base +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ build-essential \ libpq-dev \ libwebp-dev \ # LDAP Dependencies libsasl2-dev libldap2-dev libssl-dev \ gnupg gnupg2 gnupg1 \ - && rm -rf /var/lib/apt/lists/* \ - && pip install -U --no-cache-dir pip + && rm -rf /var/lib/apt/lists/* +RUN python3 -m venv --upgrade-deps $VENV_PATH -# install poetry - respects $POETRY_VERSION & $POETRY_HOME -ENV POETRY_VERSION=1.3.1 -RUN curl -sSL https://install.python-poetry.org | python3 - +# Install the wheel and all dependencies into the venv +FROM venv-builder-base AS venv-builder -# copy project requirement files here to ensure they will be cached. -WORKDIR $PYSETUP_PATH -COPY ./poetry.lock ./pyproject.toml ./ +# Copy built package (wheel) and its dependency requirements +COPY --from=packages * /dist/ -# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally -RUN poetry install -E pgsql --only main +# Install the wheel with exact versions of dependencies into the venv +RUN . $VENV_PATH/bin/activate \ + && pip install --require-hashes -r /dist/requirements.txt --find-links /dist ############################################### # CRFPP Image @@ -96,39 +145,25 @@ RUN apt-get update \ # create directory used for Docker Secrets RUN mkdir -p /run/secrets -# copying poetry and venv into image -COPY --from=builder-base $POETRY_HOME $POETRY_HOME -COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH - +# copy CRF++ and add it to the library path ENV LD_LIBRARY_PATH=/usr/local/lib COPY --from=crfpp /usr/local/lib/ /usr/local/lib COPY --from=crfpp /usr/local/bin/crf_learn /usr/local/bin/crf_learn COPY --from=crfpp /usr/local/bin/crf_test /usr/local/bin/crf_test -# copy backend -COPY ./mealie $MEALIE_HOME/mealie -COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/ +# Copy venv into image. It contains a fully-installed mealie backend and frontend. +COPY --from=venv-builder $VENV_PATH $VENV_PATH -# venv already has runtime deps installed we get a quicker install -WORKDIR $MEALIE_HOME -RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --only main -WORKDIR / # Grab CRF++ Model Release -RUN python $MEALIE_HOME/mealie/scripts/install_model.py +RUN python -m mealie.scripts.install_model VOLUME [ "$MEALIE_HOME/data/" ] ENV APP_PORT=9000 EXPOSE ${APP_PORT} -HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1 - -# ---------------------------------- -# Copy Frontend - -ENV STATIC_FILES=/spa/static -COPY --from=builder /app/dist ${STATIC_FILES} +HEALTHCHECK CMD python -m mealie.scripts.healthcheck || exit 1 ENV HOST 0.0.0.0 diff --git a/docker/entry.sh b/docker/entry.sh index 3acb00efc..cccc2ba9d 100644 --- a/docker/entry.sh +++ b/docker/entry.sh @@ -32,13 +32,51 @@ init() { cd /app # Activate our virtual environment here - . /opt/pysetup/.venv/bin/activate + . /opt/mealie/bin/activate +} + +load_secrets() { + # Each of these environment variables will support a `_FILE` suffix that allows + # for setting the environment variable through the Docker Compose secret + # pattern. + local -a secret_supported_vars=( + "POSTGRES_USER" + "POSTGRES_PASSWORD" + "POSTGRES_SERVER" + "POSTGRES_PORT" + "POSTGRES_DB" + "POSTGRES_URL_OVERRIDE" + + "SMTP_HOST" + "SMTP_PORT" + "SMTP_USER" + "SMTP_PASSWORD" + + "LDAP_SERVER_URL" + "LDAP_QUERY_PASSWORD" + + "OIDC_CONFIGURATION_URL" + "OIDC_CLIENT_ID" + "OIDC_CLIENT_SECRET" + + "OPENAI_BASE_URL" + "OPENAI_API_KEY" + ) + + # If any secrets are set, prefer them over base environment variables. + for var in "${secret_supported_vars[@]}"; do + file_var="${var}_FILE" + if [ -n "${!file_var}" ]; then + export "$var=$(<"${!file_var}")" + fi + done } change_user init +load_secrets # Start API HOST_IP=`/sbin/ip route|awk '/default/ { print $3 }'` -exec python /app/mealie/main.py +exec mealie diff --git a/docs/docs/contributors/developers-guide/building-packages.md b/docs/docs/contributors/developers-guide/building-packages.md new file mode 100644 index 000000000..5fe45e13c --- /dev/null +++ b/docs/docs/contributors/developers-guide/building-packages.md @@ -0,0 +1,40 @@ +# Building Packages + +Released packages are [built and published via GitHub actions](maintainers.md#drafting-releases). + +## Python packages + +To build Python packages locally for testing, use [`task`](starting-dev-server.md#without-dev-containers). After installing `task`, run `task py:package` to perform all the steps needed to build the package and a requirements file. To do it manually, run: +```sh +pushd frontend +yarnpkg install +yarnpkg generate +popd +rm -r mealie/frontend +cp -a frontend/dist mealie/frontend +poetry build +poetry export -n --only=main --extras=pgsql --output=dist/requirements.txt +MEALIE_VERSION=$(poetry version --short) +echo "mealie[pgsql]==${MEALIE_VERSION} \\" >> dist/requirements.txt +poetry run pip hash dist/mealie-${MEALIE_VERSION}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt +echo " \\" >> dist/requirements.txt +poetry run pip hash dist/mealie-${MEALIE_VERSION}.tar.gz | tail -n1 >> dist/requirements.txt +``` + +The Python package can be installed with all of its dependencies pinned to the versions tested by the developers with: +```sh +pip3 install -r dist/requirements.txt --find-links dist +``` + +To install with the latest but still compatible dependency versions, instead run `pip3 install dist/mealie-$VERSION-py3-none-any.whl` (where `$VERSION` is the version of mealie to install). + +## Docker image +One way to build the Docker image is to run the following command in the project root directory: +```sh +docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT=$(git rev-parse HEAD) . +``` + +The Docker image can be built from the pre-built Python packages with the task command `task docker:build-from-package`. This is equivalent to: +```sh +docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT=$(git rev-parse HEAD) --build-context packages=dist . +``` diff --git a/docs/docs/documentation/getting-started/features.md b/docs/docs/documentation/getting-started/features.md index a068ceea5..7962bb292 100644 --- a/docs/docs/documentation/getting-started/features.md +++ b/docs/docs/documentation/getting-started/features.md @@ -35,7 +35,7 @@ Mealie has a robust and flexible recipe organization system with a few different #### Categories -Categories are the overarching organizer for recipes. You can assign as many categories as you'd like to a recipe, but we recommend that you try to limit the categories you assign to a recipe to one or two. This helps keep categories as focused as possible while still allowing you to find recipes that are related to each other. For example, you might assign a recipe to the category **Breakfast**, **Lunch**, **Dinner**, or **Side**. +Categories are the overarching organizer for recipes. You can assign as many categories as you'd like to a recipe, but we recommend that you try to limit the categories you assign to a recipe to one or two. This helps keep categories as focused as possible while still allowing you to find recipes that are related to each other. For example, you might assign a recipe to the category **Breakfast**, **Lunch**, **Dinner**, **Side**, or **Drinks**. [Categories Demo](https://demo.mealie.io/g/home/recipes/categories){ .md-button .md-button--primary } @@ -84,7 +84,30 @@ The meal planner has the concept of plan rules. These offer a flexible way to us The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list or link a recipe and all of it's ingredients to track meals during the week. +Managing shopping lists can be done from the Sidebar > Shopping Lists. +Here you will be able to: +- See items already on the Shopping List +- See linked recipes with ingredients + - Toggling via the 'Pot' icon will show you the linked recipe, allowing you to click to access it. +- Check off an item +- Add / Change / Remove / Sort Items via the grid icon + - Be sure if you are modifying an ingredient to click the 'Save' icon. +- Add / Change / Remove / Sort Labels + - 'No Label' will always be on the top, others can be Reordered via the 'Reorder Labels' button + +!!! tip + If you accidentally checked off an item, you can uncheck it by expanding 'items checked' and unchecking it. This will add it back to the Shopping List. + +!!! tip + You can use Labels to categorize your ingredients. You may want to Label by Food Type (Frozen, Fresh, etc), by Store, Tool, Recipe, or more. Play around with this to see what works best for you. + +!!! tip + You can toggle 'Food' on items so that if you add multiple of the same food / ingredient, Mealie will automatically combine them together. Do this by editing an item in the Shopping List and clicking the 'Apple' icon. If you then have recipes that contain "1 | cup | cheese" and "2 | cup | cheese" this would be combined to show "3 cups of cheese." + +[See FAQ for more information](../getting-started/faq.md) + + [Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary } ## Integrations @@ -94,9 +117,9 @@ Mealie is designed to integrate with many different external services. There are ### Notifiers Notifiers are event-driven notifications sent when specific actions are performed within Mealie. Some actions include: -- creating a recipe -- adding items to a shopping list -- creating a new mealplan +- Creating / Updating a recipe +- Adding items to a shopping list +- Creating a new mealplan Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), which integrates with a large number of notification services. In addition, certain custom notifiers send basic event data to the consumer (e.g. the `id` of the resource). These include: diff --git a/docs/docs/documentation/getting-started/installation/backend-config.md b/docs/docs/documentation/getting-started/installation/backend-config.md index 6884bd6f7..819629979 100644 --- a/docs/docs/documentation/getting-started/installation/backend-config.md +++ b/docs/docs/documentation/getting-started/installation/backend-config.md @@ -31,27 +31,27 @@ ### Database -| Variables | Default | Description | -| --------------------- | :------: | ----------------------------------------------------------------------- | -| DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' | -| POSTGRES_USER | mealie | Postgres database user | -| POSTGRES_PASSWORD | mealie | Postgres database password | -| POSTGRES_SERVER | postgres | Postgres database server address | -| POSTGRES_PORT | 5432 | Postgres database port | -| POSTGRES_DB | mealie | Postgres database name | -| POSTGRES_URL_OVERRIDE | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables | + | Variables | Default | Description | + | ------------------------------------------------------- | :------: | ----------------------------------------------------------------------- | + | DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' | + | POSTGRES_USER[†][secrets] | mealie | Postgres database user | + | POSTGRES_PASSWORD[†][secrets] | mealie | Postgres database password | + | POSTGRES_SERVER[†][secrets] | postgres | Postgres database server address | + | POSTGRES_PORT[†][secrets] | 5432 | Postgres database port | + | POSTGRES_DB[†][secrets] | mealie | Postgres database name | + | POSTGRES_URL_OVERRIDE[†][secrets] | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables | ### Email -| Variables | Default | Description | -| ------------------ | :-----: | ------------------------------------------------- | -| SMTP_HOST | None | Required For email | -| SMTP_PORT | 587 | Required For email | -| SMTP_FROM_NAME | Mealie | Required For email | -| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' | -| SMTP_FROM_EMAIL | None | Required For email | -| SMTP_USER | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' | -| SMTP_PASSWORD | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' | +| Variables | Default | Description | +| ----------------------------------------------- | :-----: | ------------------------------------------------- | +| SMTP_HOST[†][secrets] | None | Required For email | +| SMTP_PORT[†][secrets] | 587 | Required For email | +| SMTP_FROM_NAME | Mealie | Required For email | +| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' | +| SMTP_FROM_EMAIL | None | Required For email | +| SMTP_USER[†][secrets] | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' | +| SMTP_PASSWORD[†][secrets] | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' | ### Webworker @@ -72,21 +72,21 @@ Use this only when mealie is run without a webserver or reverse proxy. ### LDAP -| Variables | Default | Description | -| -------------------- | :-----: | ----------------------------------------------------------------------------------------------------------------------------------- | -| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth | -| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) | -| LDAP_TLS_INSECURE | False | Do not verify server certificate when using secure LDAP | -| LDAP_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) | -| LDAP_ENABLE_STARTTLS | False | Optional. Use STARTTLS to connect to the server | -| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) | -| LDAP_QUERY_BIND | None | Optional bind user for LDAP search queries (e.g. `cn=admin,cn=users,dc=example,dc=com`). If `None` then anonymous bind will be used | -| LDAP_QUERY_PASSWORD | None | Optional password for the bind user used in LDAP_QUERY_BIND | -| LDAP_USER_FILTER | None | Optional LDAP filter to narrow down eligible users (e.g. `(memberOf=cn=mealie_user,dc=example,dc=com)`) | -| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) | -| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id | -| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name | -| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email | +| Variables | Default | Description | +| ----------------------------------------------------- | :-----: | ----------------------------------------------------------------------------------------------------------------------------------- | +| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth | +| LDAP_SERVER_URL[†][secrets] | None | LDAP server URL (e.g. ldap://ldap.example.com) | +| LDAP_TLS_INSECURE | False | Do not verify server certificate when using secure LDAP | +| LDAP_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) | +| LDAP_ENABLE_STARTTLS | False | Optional. Use STARTTLS to connect to the server | +| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) | +| LDAP_QUERY_BIND | None | Optional bind user for LDAP search queries (e.g. `cn=admin,cn=users,dc=example,dc=com`). If `None` then anonymous bind will be used | +| LDAP_QUERY_PASSWORD[†][secrets] | None | Optional password for the bind user used in LDAP_QUERY_BIND | +| LDAP_USER_FILTER | None | Optional LDAP filter to narrow down eligible users (e.g. `(memberOf=cn=mealie_user,dc=example,dc=com)`) | +| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) | +| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id | +| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name | +| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email | ### OpenID Connect (OIDC) @@ -94,23 +94,22 @@ Use this only when mealie is run without a webserver or reverse proxy. For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md) -| Variables | Default | Description | -|---------------------------------------------------|:-------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect | -| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC | -| OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration | -| OIDC_CLIENT_ID | None | The client id of your configured client in your provider | -| OIDC_CLIENT_SECRET
:octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider | -| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate. For more information see [this page](../authentication/oidc-v2.md#groups) | -| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be able to successfully authenticate *and* be made an admin. For more information see [this page](../authentication/oidc-v2.md#groups) | -| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed and you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL | -| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with " | -| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked | -| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") | -| OIDC_NAME_CLAIM | name | This is the claim which Mealie will use for the users Full Name | -| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** | -| OIDC_SCOPES_OVERRIDE | None | Advanced configuration used to override the scopes requested from the IdP. **Most users won't need to change this**. At a minimum, 'openid profile email' are required. | -| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) | +| Variables | Default | Description | +| ----------------------------------------------------------------------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect | +| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC | +| OIDC_CONFIGURATION_URL[†][secrets] | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration | +| OIDC_CLIENT_ID[†][secrets] | None | The client id of your configured client in your provider | +| OIDC_CLIENT_SECRET[†][secrets]
:octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider | +| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate, regardless of the `OIDC_ADMIN_GROUP`. For more information see [this page](../authentication/oidc.md#groups) | +| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be made an admin. For more information see [this page](../authentication/oidc.md#groups) | +| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed an you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL | +| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with " | +| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked | +| OIDC_SIGNING_ALGORITHM | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | +| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") | +| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** | +| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) | ### OpenAI @@ -119,17 +118,13 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md) Mealie supports various integrations using OpenAI. For more information, check out our [OpenAI documentation](./open-ai.md). For custom mapping variables (e.g. OPENAI_CUSTOM_HEADERS) you should pass values as JSON encoded strings (e.g. `OPENAI_CUSTOM_PARAMS='{"k1": "v1", "k2": "v2"}'`) -| Variables | Default | Description | -| ---------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| OPENAI_BASE_URL | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform | -| OPENAI_API_KEY | None | Your OpenAI API Key. Enables OpenAI-related features | -| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty | -| OPENAI_CUSTOM_HEADERS | None | Custom HTTP headers to add to all OpenAI requests. This should generally be left empty unless your custom service requires them | -| OPENAI_CUSTOM_PARAMS | None | Custom HTTP query params to add to all OpenAI requests. This should generally be left empty unless your custom service requires them | -| OPENAI_ENABLE_IMAGE_SERVICES | True | Whether to enable OpenAI image services, such as creating recipes via image. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs | -| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs | -| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs | -| OPENAI_REQUEST_TIMEOUT | 60 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware | +| Variables | Default | Description | +| ------------------------------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------- | +| OPENAI_BASE_URL[†][secrets] | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform | +| OPENAI_API_KEY[†][secrets] | None | Your OpenAI API Key. Enables OpenAI-related features | +| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty | +| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs | +| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs | ### Theming @@ -154,24 +149,80 @@ Setting the following environmental variables will change the theme of the front ### Docker Secrets -Setting a credential can be done using secrets when running in a Docker container. -This can be used to avoid leaking passwords through compose files, environment variables, or command-line history. -For example, to configure the Postgres database password in Docker compose, create a file on the host that contains only the password, and expose that file to the Mealie service as a secret with the correct name. -Note that environment variables take priority over secrets, so any previously defined environment variables should be removed when migrating to secrets. +### Docker Secrets + +> Starting in version `2.4.2`, any environment variable in the preceding lists with a dagger +> symbol next to them support the Docker Compose secrets pattern, below. +[Docker Compose secrets][docker-secrets] can be used to secure sensitive information regarding the Mealie implementation +by managing control of each secret independently from the single `.env` file. This is helpful for users that may need +different levels of access for various, sensitive environment variables, such as differentiating between hardening +operations (e.g., server endpoints and ports) and user access control (e.g., usernames, passwords, and API keys). + +To convert any of these environment variables to a Docker Compose secret, append `_FILE` to the environment variable and +connect it with a Docker Compose secret, per the [Docker documentation][docker-secrets]. + +If both the base environment variable and the secret pattern of the environment variable are set, the secret will always +take precedence. + +For example, a user that wishes to harden their operations by only giving some access to their database URL, but who +wish to place additional security around their user access control, may have a Docker Compose configuration similar to: + ```yaml services: mealie: - ... - environment: - ... - POSTGRES_USER: postgres secrets: - - POSTGRES_PASSWORD + # These secrets will be loaded by Docker into the `/run/secrets` folder within the container. + - postgres-host + - postgres-port + - postgres-db-name + - postgres-user + - postgres-password + environment: + DB_ENGINE: postgres + POSTGRES_SERVER: duplicate.entry.tld # This will be ignored, due to the secret defined, below. + POSTGRES_SERVER_FILE: /run/secrets/postgres-host + POSTGRES_PORT_FILE: /run/secrets/postgres-port + POSTGRES_DB_FILE: /run/secrets/postgres-db-name + POSTGRES_USER_FILE: /run/secrets/postgres-user + POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password + +# Each of these secrets are loaded via these local files. Different patterns are available. See the Docker Compose +# documentation for more information. secrets: - POSTGRES_PASSWORD: - file: postgrespassword.txt + postgres-host: + file: ./secrets/postgres-host.txt + postgres-port: + file: ./secrets/postgres-port.txt + postgres-db-name: + file: ./secrets/sensitive/postgres-db-name.txt + postgres-user: + file: ./secrets/sensitive/postgres-user.txt + postgres-password: + file: ./secrets/sensitive/postgres-password.txt +``` +In the example above, a directory organization and access pattern may look like the following: +```text +. +โ”œโ”€โ”€ docker-compose.yml +โ””โ”€โ”€ secrets # Access restricted to anyone that can manage secrets + โ”œโ”€โ”€ postgres-host.txt + โ”œโ”€โ”€ postgres-port.txt + โ””โ”€โ”€ sensitive # Access further-restricted to anyone managing service accounts + โ”œโ”€โ”€ postgres-db-name.txt + โ”œโ”€โ”€ postgres-password.txt + โ””โ”€โ”€ postgres-user.txt ``` +How you organize your secrets is ultimately up to you. At minimum, it's highly recommended to use secret patterns for +at least these sensitive environment variables when working within shared environments: + +- `POSTGRES_PASSWORD` +- `SMTP_PASSWORD` +- `LDAP_QUERY_PASSWORD` +- `OPENAI_API_KEY` + +[docker-secrets]: https://docs.docker.com/compose/use-secrets/ +[secrets]: #docker-secrets [unicorn_workers]: https://www.uvicorn.org/deployment/#built-in diff --git a/docs/docs/documentation/getting-started/installation/installation-checklist.md b/docs/docs/documentation/getting-started/installation/installation-checklist.md index 9cc4f12cb..16277a4dd 100644 --- a/docs/docs/documentation/getting-started/installation/installation-checklist.md +++ b/docs/docs/documentation/getting-started/installation/installation-checklist.md @@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do: 1. Take a backup just in case! -2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.5.0` +2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.6.0` 3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access. 4. Restart the container diff --git a/docs/docs/documentation/getting-started/installation/postgres.md b/docs/docs/documentation/getting-started/installation/postgres.md index 9c2062440..f845bd74f 100644 --- a/docs/docs/documentation/getting-started/installation/postgres.md +++ b/docs/docs/documentation/getting-started/installation/postgres.md @@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In ```yaml services: mealie: - image: ghcr.io/mealie-recipes/mealie:v2.5.0 # (3) + image: ghcr.io/mealie-recipes/mealie:v2.6.0 # (3) container_name: mealie restart: always ports: diff --git a/docs/docs/documentation/getting-started/installation/sqlite.md b/docs/docs/documentation/getting-started/installation/sqlite.md index d1a92c09b..a5dc2e5cf 100644 --- a/docs/docs/documentation/getting-started/installation/sqlite.md +++ b/docs/docs/documentation/getting-started/installation/sqlite.md @@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th ```yaml services: mealie: - image: ghcr.io/mealie-recipes/mealie:v2.5.0 # (3) + image: ghcr.io/mealie-recipes/mealie:v2.6.0 # (3) container_name: mealie restart: always ports: diff --git a/docs/docs/documentation/getting-started/usage/backups-and-restoring.md b/docs/docs/documentation/getting-started/usage/backups-and-restoring.md index 84b9317b2..7f10ea786 100644 --- a/docs/docs/documentation/getting-started/usage/backups-and-restoring.md +++ b/docs/docs/documentation/getting-started/usage/backups-and-restoring.md @@ -1,17 +1,24 @@ -# Backups and Restoring +# Backups and Restores -Mealie provides an integrated mechanics for doing full installation backups of the database. Navigate to `/admin/backups` to +Mealie provides an integrated mechanic for doing full installation backups of the database. + +Navigate to Settings > Backups or manually by adding `/admin/backups` to your instance URL. + +From this page, you will be able to: - See a list of available backups -- Perform a backups -- Restore a backup +- Create a backup +- Upload a backup +- Delete a backup (Confirmation Required) +- Download a backup +- Perform a restore !!! tip If you're using Mealie with SQLite all your data is stored in the /app/data/ folder in the container. You can easily perform entire site backups by stopping the container, and backing up this folder with your chosen tool. This is the **best** way to backup your data. ## Restoring from a Backup -To restore from a backup it needs to be uploaded to your instance, this can be done through the web portal. On the lower left hand corner of the backups data table you'll see an upload button. Click this button and select the backup file you want to upload and it will be available to import shortly. +To restore from a backup it needs to be uploaded to your instance which can be done through the web portal. On the top left of the page you'll see an upload button. Click this button and select the backup file you want to upload and it will be available to import shortly. You can alternatively use one of the backups you see on the screen, if one exists. Before importing it's critical that you understand the following: @@ -19,6 +26,9 @@ Before importing it's critical that you understand the following: - This action cannot be undone - If this action is successful you will be logged out and you will need to log back in to complete the restore +!!! tip + If for some reason the restore does not succeed, you can review the logs of what the issue may be, download the backup .ZIP and edit the contents of database.json to potentially resolve the issue. For example, if you receive an error restoring 'shopping-list' you can edit out the contents of that list while allowing other sections to restore. If you would like any assistance on this, reach out over Discord. + !!! warning Prior to beta-v5 using a mis-matched version of the database backup will result in an error that will prevent you from using the instance of Mealie requiring you to remove all data and reinstall. Post beta-v5 performing a mismatched restore will throw an error and alert the user of the issue. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 68110a2c2..64a86f4da 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -99,6 +99,7 @@ nav: - Non-Code: "contributors/non-coders.md" - Translating: "contributors/translating.md" - Developers Guide: + - Building Packages: "contributors/developers-guide/building-packages.md" - Code Contributions: "contributors/developers-guide/code-contributions.md" - Dev Getting Started: "contributors/developers-guide/starting-dev-server.md" - Database Changes: "contributors/developers-guide/database-changes.md" diff --git a/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue b/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue index da9ca4427..fe9a399f6 100644 --- a/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue +++ b/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue @@ -34,6 +34,7 @@ :label="$t('shopping-list.note')" rows="1" auto-grow + autofocus @keypress="handleNoteKeyPress" > @@ -80,37 +81,37 @@ + + + - - - @@ -139,6 +140,11 @@ export default defineComponent({ type: Array as () => IngredientFood[], required: true, }, + allowDelete: { + type: Boolean, + required: false, + default: true, + }, }, setup(props, context) { const foodStore = useFoodStore(); diff --git a/frontend/lang/messages/ar-SA.json b/frontend/lang/messages/ar-SA.json index 1e4f676af..51a6ace4a 100644 --- a/frontend/lang/messages/ar-SA.json +++ b/frontend/lang/messages/ar-SA.json @@ -550,7 +550,7 @@ "yields-amount-with-text": "Yields {amount} {text}", "yield-text": "Yield Text", "quantity": "Quantity", - "choose-unit": "Choose Unit", + "choose-unit": "ุงุฎุชุฑ ุงู„ูˆุญุฏุฉ", "press-enter-to-create": "Press Enter to Create", "choose-food": "Choose Food", "notes": "Notes", diff --git a/frontend/lang/messages/gl-ES.json b/frontend/lang/messages/gl-ES.json index bdc2f9093..1353d03fc 100644 --- a/frontend/lang/messages/gl-ES.json +++ b/frontend/lang/messages/gl-ES.json @@ -182,7 +182,7 @@ "date": "Data", "id": "Id", "owner": "Dono", - "change-owner": "Change Owner", + "change-owner": "Mudar Proprietario", "date-added": "Engadida o", "none": "Nada", "run": "Executar", @@ -214,10 +214,10 @@ "confirm-delete-generic-items": "Estรกs seguro de que queres eliminar os seguintes elementos?", "organizers": "Organizadores", "caution": "Coidado", - "show-advanced": "Show Advanced", - "add-field": "Add Field", + "show-advanced": "Mostrar Avanzadas", + "add-field": "Adicionar Campo", "date-created": "Date Created", - "date-updated": "Date Updated" + "date-updated": "Data de Atualizaciรณn" }, "group": { "are-you-sure-you-want-to-delete-the-group": "Estรกs seguro de que queres eliminar {groupName}?", @@ -295,11 +295,11 @@ "private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings", "lock-recipe-edits-from-other-households": "Lock recipe edits from other households", "lock-recipe-edits-from-other-households-description": "When enabled only users in your household can edit recipes created by your household", - "household-recipe-preferences": "Household Recipe Preferences", + "household-recipe-preferences": "Preferencias de receitas da casa", "default-recipe-preferences-description": "These are the default settings when a new recipe is created in your household. These can be changed for individual recipes in the recipe settings menu.", "allow-users-outside-of-your-household-to-see-your-recipes": "Allow users outside of your household to see your recipes", "allow-users-outside-of-your-household-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your household or with a pre-generated private link", - "household-preferences": "Household Preferences" + "household-preferences": "Preferencias da Casa" }, "meal-plan": { "create-a-new-meal-plan": "Crea un Novo Menรบ", @@ -322,9 +322,9 @@ "mealplan-update-failed": "Produciuse un erro na actualizaciรณn do menรบ", "mealplan-updated": "Menรบ Actualizado", "mealplan-households-description": "If no household is selected, recipes can be added from any household", - "any-category": "Any Category", - "any-tag": "Any Tag", - "any-household": "Any Household", + "any-category": "Calquer Categoria", + "any-tag": "Calquer Etiqueta", + "any-household": "Calquer Casa", "no-meal-plan-defined-yet": "Aรญnda non se definiu ningรบn menรบ", "no-meal-planned-for-today": "Non hai ningunha comida prevista para hoxe", "numberOfDays-hint": "Nรบmero de dรญas ao cargar a pรกxina", @@ -395,7 +395,7 @@ }, "tandoor": { "description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.", - "title": "Tandoor Recipes" + "title": "Receitas do Tandoor" }, "recipe-data-migrations": "Recipe Data Migrations", "recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.", @@ -404,8 +404,8 @@ "tag-all-recipes": "Tag all recipes with {tag-name} tag", "nextcloud-text": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.", "chowdown-text": "Mealie admite de forma nativa o formato do repositorio de chowdown. Descarga o repositorio de cรณdigos como ficheiro .zip e cรกrgao a continuaciรณn.", - "recipe-1": "Recipe 1", - "recipe-2": "Recipe 2", + "recipe-1": "Receita 1", + "recipe-2": "Receita 2", "paprika-text": "Mealie can import recipes from the Paprika application. Export your recipes from paprika, rename the export extension to .zip and upload it below.", "mealie-text": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.", "plantoeat": { @@ -424,15 +424,15 @@ "new-recipe": { "bulk-add": "Bulk Add", "error-details": "Only websites containing ld+json or microdata can be imported by Mealie. Most major recipe websites support this data structure. If your site cannot be imported but there is json data in the log, please submit a github issue with the URL and data.", - "error-title": "Looks Like We Couldn't Find Anything", - "from-url": "Import a Recipe", - "github-issues": "GitHub Issues", + "error-title": "Parece que non conseguimos encontrar nada", + "from-url": "Importar unha Receita", + "github-issues": "Problemas no GitHub", "google-ld-json-info": "Google ld+json Info", - "must-be-a-valid-url": "Must be a Valid URL", - "paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Paste in your recipe data. Each line will be treated as an item in a list", - "recipe-markup-specification": "Recipe Markup Specification", - "recipe-url": "Recipe URL", - "recipe-html-or-json": "Recipe HTML or JSON", + "must-be-a-valid-url": "Precisa ser un URL vรกlido", + "paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Pegue os datos da sua receita. Cada liรฑa serรก tratada como un item nunha lista", + "recipe-markup-specification": "Especificaciรณn Markup da Receita", + "recipe-url": "URL da Receita", + "recipe-html-or-json": "Receita en HTML ou JSON", "upload-a-recipe": "Upload a Recipe", "upload-individual-zip-file": "Upload an individual .zip file exported from another Mealie instance.", "url-form-hint": "Copy and paste a link from your favorite recipe website", @@ -440,13 +440,13 @@ "trim-whitespace-description": "Trim leading and trailing whitespace as well as blank lines", "trim-prefix-description": "Trim first character from each line", "split-by-numbered-line-description": "Attempts to split a paragraph by matching '1)' or '1.' patterns", - "import-by-url": "Import a recipe by URL", + "import-by-url": "Importar unha receita por URL", "create-manually": "Create a recipe manually", "make-recipe-image": "Make this the recipe image" }, "page": { - "404-page-not-found": "404 Page not found", - "all-recipes": "All Recipes", + "404-page-not-found": "404 Pรกxina non encontrada", + "all-recipes": "Todas as receitas", "new-page-created": "New page created", "page": "Pรกxina", "page-creation-failed": "Produciuse un erro ao creala pรกxina", @@ -467,7 +467,7 @@ "calories-suffix": "calorรญas", "carbohydrate-content": "Carbohidratos", "categories": "Categorรญas", - "cholesterol-content": "Cholesterol", + "cholesterol-content": "Colesterol", "comment-action": "Comentar", "comment": "Comentario", "comments": "Comentarios", @@ -535,7 +535,7 @@ "add-recipe-to-mealplan": "Add Recipe to Mealplan", "entry-type": "Entry Type", "date-format-hint": "Formato MM/DD/YYYY", - "date-format-hint-yyyy-mm-dd": "YYYY-MM-DD format", + "date-format-hint-yyyy-mm-dd": "Formato AAAA-MM-DD", "add-to-list": "Add to List", "add-to-plan": "Add to Plan", "add-to-timeline": "Add to Timeline", @@ -553,7 +553,7 @@ "choose-unit": "Choose Unit", "press-enter-to-create": "Press Enter to Create", "choose-food": "Choose Food", - "notes": "Notes", + "notes": "Notas", "toggle-section": "Toggle Section", "see-original-text": "See Original Text", "original-text-with-value": "Original Text: {originalText}", @@ -1194,8 +1194,8 @@ "demo_password": "Password: {password}" }, "ocr-editor": { - "ocr-editor": "Ocr editor", - "toolbar": "Toolbar", + "ocr-editor": "Editor OCR", + "toolbar": "Barra de ferramentas", "selection-mode": "Selection mode", "pan-and-zoom-picture": "Pan and zoom picture", "split-text": "Split text", @@ -1203,7 +1203,7 @@ "split-by-block": "Split by text block", "flatten": "Flatten regardless of original formating", "help": { - "help": "Help", + "help": "Axuda", "mouse-modes": "Mouse modes", "selection-mode": "Selection Mode (default)", "selection-mode-desc": "The selection mode is the main mode that can be used to enter data:", diff --git a/frontend/lang/messages/pt-BR.json b/frontend/lang/messages/pt-BR.json index 47b1abc16..df0d384ee 100644 --- a/frontend/lang/messages/pt-BR.json +++ b/frontend/lang/messages/pt-BR.json @@ -8,7 +8,7 @@ "database-type": "Tipo do Banco de Dados", "database-url": "URL do banco de dados", "default-group": "Grupo Padrรฃo", - "default-household": "Default Household", + "default-household": "Casa Padrรฃo", "demo": "Demonstraรงรฃo", "demo-status": "Status da Demonstraรงรฃo", "development": "Desenvolvimento", @@ -182,7 +182,7 @@ "date": "Data", "id": "Id", "owner": "Proprietรกrio", - "change-owner": "Change Owner", + "change-owner": "Alterar Dono", "date-added": "Data de inclusรฃo", "none": "Nenhum", "run": "Executar", @@ -214,10 +214,10 @@ "confirm-delete-generic-items": "Tem certeza que quer excluir os itens seguintes?", "organizers": "Organizadores", "caution": "Cuidado", - "show-advanced": "Show Advanced", - "add-field": "Add Field", - "date-created": "Date Created", - "date-updated": "Date Updated" + "show-advanced": "Mostrar Avanรงado", + "add-field": "Adicionar Campo", + "date-created": "Data de Criaรงรฃo", + "date-updated": "Data de Atualizaรงรฃo" }, "group": { "are-you-sure-you-want-to-delete-the-group": "Tem certeza que deseja excluir o grupo {groupName}?", @@ -244,9 +244,9 @@ "keep-my-recipes-private-description": "Define o seu grupo e todas as receitas padrรฃo como privadas. Vocรช sempre pode mudar isso mais tarde." }, "manage-members": "Gerenciar membros", - "manage-members-description": "Manage the permissions of the members in your household. {manage} allows the user to access the data-management page, and {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.", + "manage-members-description": "Gerencie as permissรตes dos membros da sua casa. {manage} permite ao usuรกrio acessar a pรกgina de gerenciamento de dados, e {invite} permite ao usuรกrio gerar links de convites para outros usuรกrios. Os proprietรกrios de grupos nรฃo podem mudar suas prรณprias permissรตes.", "manage": "Gerenciar", - "manage-household": "Manage Household", + "manage-household": "Gerenciar Casa", "invite": "Convidar", "looking-to-update-your-profile": "Procurando atualizar seu perfil?", "default-recipe-preferences-description": "Estas sรฃo as configuraรงรตes padrรฃo quando uma nova receita รฉ criada no seu grupo. Elas podem ser alteradas para receitas individuais no menu de configuraรงรตes de receitas.", @@ -267,7 +267,7 @@ "disable-users-from-commenting-on-recipes": "Desabilitar usuรกrios de comentar em receitas", "disable-users-from-commenting-on-recipes-description": "Oculta a seรงรฃo de comentรกrios na pรกgina de receita e desativa os comentรกrios", "disable-organizing-recipe-ingredients-by-units-and-food": "Desativar a organizaรงรฃo de ingredientes de receita por unidades e alimentos", - "disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields", + "disable-organizing-recipe-ingredients-by-units-and-food-description": "Oculta os campos Comida, Unidade e Quantidade de ingredientes e trata os ingredientes como campos de texto simples", "general-preferences": "Preferรชncias Gerais", "group-recipe-preferences": "Preferรชncias de Grupo de Receitas", "report": "Denunciar", @@ -280,8 +280,8 @@ "you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household" }, "household": { - "household": "Household", - "households": "Households", + "household": "Casa", + "households": "Casas", "user-household": "User Household", "create-household": "Create Household", "household-name": "Household Name", @@ -467,7 +467,7 @@ "calories-suffix": "calorias", "carbohydrate-content": "Carboidrato", "categories": "Categorias", - "cholesterol-content": "Cholesterol", + "cholesterol-content": "Colesterol", "comment-action": "Comentรกrio", "comment": "Comentรกrio", "comments": "Comentรกrios", @@ -514,11 +514,11 @@ "recipe-updated": "Receita atualizada", "remove-from-favorites": "Remover dos Favoritos", "remove-section": "Remover Seรงรฃo", - "saturated-fat-content": "Saturated fat", + "saturated-fat-content": "Gorduras saturadas", "save-recipe-before-use": "Salve a receita antes de utilizar", "section-title": "Tรญtulo da Seรงรฃo", "servings": "Porรงรตes", - "serves-amount": "Serves {amount}", + "serves-amount": "Serve {amount}", "share-recipe-message": "Eu quero compartilhar minha receita de {0} com vocรช.", "show-nutrition-values": "Mostrar informaรงรตes nutricionais", "sodium-content": "Sรณdio", @@ -528,7 +528,7 @@ "total-time": "Tempo Total", "trans-fat-content": "Trans-fat", "unable-to-delete-recipe": "Nรฃo foi possรญvel apagar a receita", - "unsaturated-fat-content": "Unsaturated fat", + "unsaturated-fat-content": "Gorduras nรฃo saturadas", "no-recipe": "Nenhuma Receita", "locked-by-owner": "Bloqueado pelo Proprietรกrio", "join-the-conversation": "Participe da conversa", @@ -600,9 +600,9 @@ "create-recipe-from-an-image": "Create Recipe from an Image", "create-recipe-from-an-image-description": "Create a recipe by uploading an image of it. Mealie will attempt to extract the text from the image using AI and create a recipe from it.", "crop-and-rotate-the-image": "Crop and rotate the image so that only the text is visible, and it's in the correct orientation.", - "create-from-image": "Create from Image", - "should-translate-description": "Translate the recipe into my language", - "please-wait-image-procesing": "Please wait, the image is processing. This may take some time.", + "create-from-image": "Criar a partir da imagem", + "should-translate-description": "Traduza a receita para o meu idioma", + "please-wait-image-procesing": "Por favor aguarde, a imagem estรก sendo processada. Isto pode levar algum tempo.", "bulk-url-import": "Importaรงรฃo de URL em massa", "debug-scraper": "Debug Scraper", "create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crie uma receita fornecendo o nome. Todas as receitas devem ter nomes exclusivos.", @@ -611,7 +611,7 @@ "scrape-recipe-description": "Scrape uma receita por url. Forneรงa o Url para o site que vocรช deseja scrape, e Mealie tentarรก raspar a receita desse site e adicionรก-la ร  sua coleรงรฃo.", "scrape-recipe-have-a-lot-of-recipes": "Tem muitas receitas a extrair de uma vez?", "scrape-recipe-suggest-bulk-importer": "Tente o importador em massa", - "scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?", + "scrape-recipe-have-raw-html-or-json-data": "Tem dados HTML ou JSON brutos?", "scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly", "import-original-keywords-as-tags": "Importar palavras-chave originais como marcadores", "stay-in-edit-mode": "Permanecer no modo de ediรงรฃo", @@ -662,22 +662,22 @@ "no-food": "Sem Comida" }, "reset-servings-count": "Reset Servings Count", - "not-linked-ingredients": "Additional Ingredients" + "not-linked-ingredients": "Ingredientes adicionais" }, "recipe-finder": { - "recipe-finder": "Recipe Finder", - "recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.", - "selected-ingredients": "Selected Ingredients", - "no-ingredients-selected": "No ingredients selected", - "missing": "Missing", - "no-recipes-found": "No recipes found", + "recipe-finder": "Localizador de Receitas", + "recipe-finder-description": "Procure por receitas baseadas em ingredientes que vocรช tem na mรฃo. Vocรช tambรฉm pode filtrar por ferramentas disponรญveis e definir um nรบmero mรกximo de ingredientes ou ferramentas que faltam.", + "selected-ingredients": "Ingredientes selecionados", + "no-ingredients-selected": "Nenhum ingrediente selecionado", + "missing": "Ausente", + "no-recipes-found": "Nenhuma receita encontrada", "no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters", "include-ingredients-on-hand": "Include Ingredients On Hand", "include-tools-on-hand": "Include Tools On Hand", "max-missing-ingredients": "Max Missing Ingredients", "max-missing-tools": "Max Missing Tools", - "selected-tools": "Selected Tools", - "other-filters": "Other Filters", + "selected-tools": "Ferramentas Selecionadas", + "other-filters": "Outros Filtros", "ready-to-make": "Ready to Make", "almost-ready-to-make": "Almost Ready to Make" }, @@ -690,7 +690,7 @@ "or": "Ou", "has-any": "Tem alguma", "has-all": "Tem todos", - "clear-selection": "Clear Selection", + "clear-selection": "Limpar Seleรงรฃo", "results": "Resultados", "search": "Pesquisar", "search-mealie": "Pesquisar no Mealie (pressione /)", @@ -884,7 +884,7 @@ "are-you-sure-you-want-to-check-all-items": "Tem certeza que deseja marcar todos os itens?", "are-you-sure-you-want-to-uncheck-all-items": "Tem certeza que deseja desmarcar todos os itens?", "are-you-sure-you-want-to-delete-checked-items": "Tem certeza que deseja apagar todos os itens marcados?", - "no-shopping-lists-found": "No Shopping Lists Found" + "no-shopping-lists-found": "Nenhuma lista de compras encontrada" }, "sidebar": { "all-recipes": "Todas as Receitas", @@ -1286,17 +1286,17 @@ "restore-from-v1-backup": "Tem uma cรณpia de seguranรงa de uma instรขncia anterior do Mealie v1? Vocรช pode restaurรก-la aqui.", "manage-profile-or-get-invite-link": "Gerencie seu prรณprio perfil, ou pegue um link de convite para compartilhar." }, - "debug-openai-services": "Debug OpenAI Services", - "debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.", - "run-test": "Run Test", - "test-results": "Test Results", + "debug-openai-services": "Debug de Serviรงos OpenAI", + "debug-openai-services-description": "Use esta pรกgina para depurar serviรงos OpenAI. Vocรช pode testar a sua conexรฃo OpenAI e ver os resultados aqui. Se vocรช tiver os serviรงos de imagem ativados, vocรช tambรฉm pode fornecer uma imagem.", + "run-test": "Executar teste", + "test-results": "Resultados do teste", "group-delete-note": "Groups with users or households cannot be deleted", "household-delete-note": "Households with users cannot be deleted" }, "profile": { "welcome-user": "๐Ÿ‘‹ Bem-vindo(a), {0}!", "description": "Gerencie seu perfil, receitas e configuraรงรตes de grupo.", - "invite-link": "Invite Link", + "invite-link": "Link de convite", "get-invite-link": "Obter link de convite", "get-public-link": "Obter link pรบblico", "account-summary": "Resumo da conta", @@ -1367,21 +1367,21 @@ "or": "OR" }, "relational-operators": { - "equals": "equals", - "does-not-equal": "does not equal", - "is-greater-than": "is greater than", - "is-greater-than-or-equal-to": "is greater than or equal to", - "is-less-than": "is less than", - "is-less-than-or-equal-to": "is less than or equal to" + "equals": "igual a", + "does-not-equal": "nรฃo รฉ igual a", + "is-greater-than": "รฉ maior que", + "is-greater-than-or-equal-to": "รฉ maior ou igual a", + "is-less-than": "รฉ menor que", + "is-less-than-or-equal-to": "รฉ menor ou igual a" }, "relational-keywords": { - "is": "is", - "is-not": "is not", - "is-one-of": "is one of", - "is-not-one-of": "is not one of", - "contains-all-of": "contains all of", - "is-like": "is like", - "is-not-like": "is not like" + "is": "รฉ", + "is-not": "nรฃo รฉ", + "is-one-of": "รฉ um(a) de", + "is-not-one-of": "nรฃo รฉ um de", + "contains-all-of": "contรฉm todos de", + "is-like": "รฉ como", + "is-not-like": "nรฃo รฉ como" } } } diff --git a/frontend/lang/messages/sv-SE.json b/frontend/lang/messages/sv-SE.json index 506660010..988f3c1d1 100644 --- a/frontend/lang/messages/sv-SE.json +++ b/frontend/lang/messages/sv-SE.json @@ -45,7 +45,7 @@ "category-filter": "Kategorifilter", "category-update-failed": "Kategori gick inte att uppdatera", "category-updated": "Kategori uppdaterad", - "uncategorized-count": "Ingen Kategori {count}", + "uncategorized-count": "Utan kategori {count}", "create-a-category": "Skapa kategori", "category-name": "Kategorinamn", "category": "Kategori" diff --git a/frontend/lang/messages/uk-UA.json b/frontend/lang/messages/uk-UA.json index dc65a750c..deb739168 100644 --- a/frontend/lang/messages/uk-UA.json +++ b/frontend/lang/messages/uk-UA.json @@ -5,7 +5,7 @@ "api-docs": "ะ”ะพะบัƒะผะตะฝั‚ะฐั†ั–ั API", "api-port": "ะŸะพั€ั‚ API", "application-mode": "ะ ะตะถะธะผ ะดะพะดะฐั‚ะบัƒ", - "database-type": "ะขะธะฟ ะฑะฐะทะธ ะดะฐะฝะฝะธั…", + "database-type": "ะขะธะฟ ะฑะฐะทะธ ะดะฐะฝะธั…", "database-url": "URL-ะฐะดั€ะตัะฐ ะฑะฐะทะธ ะดะฐะฝะธั…", "default-group": "ะ“ั€ัƒะฟะธ ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ", "default-household": "ะกั–ะผสผั ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ", @@ -190,7 +190,7 @@ "a-name-is-required": "ะะตะพะฑั…ั–ะดะฝะพ ะฒะบะฐะทะฐั‚ะธ ะฝะฐะทะฒัƒ", "delete-with-name": "ะ’ะธะดะฐะปะธั‚ะธ {name}", "confirm-delete-generic-with-name": "ะ’ะธ ะดั–ะนัะฝะพ ั…ะพั‡ะตั‚ะต ะฒะธะดะฐะปะธั‚ะธ {name}?", - "confirm-delete-own-admin-account": "ะ—ะฒะตั€ะฝั–ั‚ัŒ ัƒะฒะฐะณัƒ, ั‰ะพ ะฒะธ ะฝะฐะผะฐะณะฐั”ั‚ะตัั ะฒะธะดะฐะปะธั‚ะธ ัะฒั–ะน ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั ะฐะดะผั–ะฝั–ัั‚ั€ะฐั‚ะพั€ะฐ! ะฆัŽ ะดั–ัŽ ะฝะตะผะพะถะปะธะฒะพ ัะบะฐััƒะฒะฐั‚ะธ ั– ะฒะธ ะพัั‚ะฐั‚ะพั‡ะฝะพ ะฒะธะดะฐะปะธั‚ะต ะฒะฐัˆ ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั?", + "confirm-delete-own-admin-account": "ะ—ะฒะตั€ะฝั–ั‚ัŒ ัƒะฒะฐะณัƒ, ั‰ะพ ะฒะธ ะฝะฐะผะฐะณะฐั”ั‚ะตัั ะฒะธะดะฐะปะธั‚ะธ ัะฒั–ะน ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั ะฐะดะผั–ะฝั–ัั‚ั€ะฐั‚ะพั€ะฐ! ะฆัŽ ะดั–ัŽ ะฝะตะผะพะถะปะธะฒะพ ัะบะฐััƒะฒะฐั‚ะธ ะน ะฒะธ ะพัั‚ะฐั‚ะพั‡ะฝะพ ะฒะธะดะฐะปะธั‚ะต ะฒะฐัˆ ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั?", "organizer": "ะžั€ะณะฐะฝั–ะทะฐั‚ะพั€", "transfer": "ะŸะตั€ะตะดะฐั‡ะฐ", "copy": "ะกะบะพะฟั–ัŽะฒะฐั‚ะธ", @@ -200,7 +200,7 @@ "learn-more": "ะ”ั–ะทะฝะฐั‚ะธัั ะฑั–ะปัŒัˆะต", "this-feature-is-currently-inactive": "ะฆั ั„ัƒะฝะบั†ั–ั ะฝะฐั€ะฐะทั– ะฝะต ะฐะบั‚ะธะฒะฝะฐ", "clipboard-not-supported": "ะ‘ัƒั„ะตั€ ะพะฑะผั–ะฝัƒ ะฝะต ะฟั–ะดั‚ั€ะธะผัƒั”ั‚ัŒัั", - "copied-to-clipboard": "ะกะบะพะฟั–ะนะพะฒะฐะฝะพ ะดะพ ะฑัƒั„ะตั€ัƒ ะพะฑะผั–ะฝัƒ", + "copied-to-clipboard": "ะกะบะพะฟั–ะนะพะฒะฐะฝะพ ะดะพ ะฑัƒั„ะตั€ะฐ ะพะฑะผั–ะฝัƒ", "your-browser-does-not-support-clipboard": "ะ’ะฐัˆ ะฑั€ะฐัƒะทะตั€ ะฝะต ะฟั–ะดั‚ั€ะธะผัƒั” ะฑัƒั„ะตั€ ะพะฑะผั–ะฝัƒ", "copied-items-to-clipboard": "ะ–ะพะดะตะฝ ะตะปะตะผะตะฝั‚ ะฝะต ัะบะพะฟั–ะนะพะฒะฐะฝะพ ะฒ ะฑัƒั„ะตั€ ะพะฑะผั–ะฝัƒ|ะžะดะธะฝ ะตะปะตะผะตะฝั‚ ัะบะพะฟั–ะนะพะฒะฐะฝะพ ะฒ ะฑัƒั„ะตั€ ะพะฑะผั–ะฝัƒ|ะกะบะพะฟั–ะนะพะฒะฐะฝะพ {count} ะตะปะตะผะตะฝั‚ั–ะฒ ะฒ ะฑัƒั„ะตั€ ะพะฑะผั–ะฝัƒ", "actions": "ะ”ั–ั—", @@ -246,14 +246,14 @@ "manage-members": "ะšะตั€ัƒะฒะฐะฝะฝั ะšะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผะธ", "manage-members-description": "ะšะตั€ัƒะนั‚ะต ะดะพะทะฒะพะปะฐะผะธ ัƒั‡ะฐัะฝะธะบั–ะฒ ะฒะฐัˆะพั— ัั–ะผสผั—. {manage} ะดะพะทะฒะพะปัั” ะบะพั€ะธัั‚ัƒะฒะฐั‡ะตะฒั– ะพั‚ั€ะธะผะฐั‚ะธ ะดะพัั‚ัƒะฟ ะดะพ ัั‚ะพั€ั–ะฝะบะธ ะบะตั€ัƒะฒะฐะฝะฝั ะดะฐะฝะธะผะธ {invite} ะดะพะทะฒะพะปัั” ะบะพั€ะธัั‚ัƒะฒะฐั‡ะตะฒั– ะณะตะฝะตั€ัƒะฒะฐั‚ะธ ะฟะพัะธะปะฐะฝะฝั ะทะฐะฟั€ะพัˆะตะฝะฝั ะดะปั ั–ะฝัˆะธั… ะบะพั€ะธัั‚ัƒะฒะฐั‡ั–ะฒ. ะ’ะปะฐัะฝะธะบะธ ะณั€ัƒะฟะธ ะฝะต ะผะพะถัƒั‚ัŒ ะทะผั–ะฝะธั‚ะธ ะฒะปะฐัะฝั– ะดะพะทะฒะพะปะธ.", "manage": "ะšะตั€ัƒะฒะฐะฝะฝั", - "manage-household": "Manage Household", + "manage-household": "ะšะตั€ัƒะฒะฐั‚ะธ ัั–ะผสผั”ัŽ", "invite": "ะ—ะฐะฟั€ะพัˆะตะฝะฝั", "looking-to-update-your-profile": "ะ‘ะฐะถะฐั”ั‚ะต ะพะฝะพะฒะธั‚ะธ ัะฒั–ะน ะฟั€ะพั„ั–ะปัŒ?", "default-recipe-preferences-description": "ะฆะต ั‚ะธะฟะพะฒั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั, ะบะพะปะธ ัั‚ะฒะพั€ัŽั”ั‚ัŒัั ะฝะพะฒะธะน ั€ะตั†ะตะฟั‚ ัƒ ะฒะฐัˆั–ะน ะณั€ัƒะฟั–. ะฆั– ะฟะฐั€ะฐะผะตั‚ั€ะธ ะผะพะถะฝะฐ ะทะผั–ะฝะธั‚ะธ ะดะปั ะพะบั€ะตะผะธั… ั€ะตั†ะตะฟั‚ั–ะฒ ะฒ ะผะตะฝัŽ ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝัŒ ั€ะตั†ะตะฟั‚ั–ะฒ.", "default-recipe-preferences": "ะŸะฐั€ะฐะผะตั‚ั€ะธ ะทะฐ ัƒะผะพะฒั‡ะฐะฝะฝัะผ", "group-preferences": "ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะณั€ัƒะฟะธ", "private-group": "ะŸั€ะธะฒะฐั‚ะฝะฐ ะณั€ัƒะฟะฐ", - "private-group-description": "Setting your group to private will disable all public view options. This overrides any individual public view settings", + "private-group-description": "ะฏะบั‰ะพ ะทั€ะพะฑะธั‚ะธ ะณั€ัƒะฟัƒ ะฟั€ะธะฒะฐั‚ะฝะพัŽ, ั‚ะพ ะฒัั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟัƒะฑะปั–ั‡ะฝะพะณะพ ะฟะตั€ะตะณะปัะดัƒ ะฑัƒะดะต ัะบะธะฝัƒั‚ะพ. ะฆะต ะทะฐะผั–ะฝะธั‚ัŒ ั–ะฝะดะธะฒั–ะดัƒะฐะปัŒะฝั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟัƒะฑะปั–ั‡ะฝะพะณะพ ะฟะตั€ะตะณะปัะดัƒ", "enable-public-access": "ะ”ะพะทะฒะพะปะธั‚ะธ ะทะฐะณะฐะปัŒะฝะธะน ะดะพัั‚ัƒะฟ", "enable-public-access-description": "ะ ะพะฑะธั‚ัŒ ะณั€ัƒะฟะพะฒั– ั€ะตั†ะตะฟั‚ะธ ะทะฐะณะฐะปัŒะฝะพะดะพัั‚ัƒะฟะฝะธะผะธ ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ ั– ะดะพะทะฒะพะปัั” ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผ ะฟะตั€ะตะณะปัะดะฐั‚ะธ ั€ะตั†ะตะฟั‚ะธ ะฑะตะท ะฒั…ะพะดัƒ ะฒ ัะธัั‚ะตะผัƒ", "allow-users-outside-of-your-group-to-see-your-recipes": "ะ”ะพะทะฒะพะปะธั‚ะธ ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผ ะทะฐ ะผะตะถะฐะผะธ ะฒะฐัˆะพั— ะณั€ัƒะฟะธ ะฑะฐั‡ะธั‚ะธ ะฒะฐัˆั– ั€ะตั†ะตะฟั‚ะธ", @@ -277,7 +277,7 @@ "admin-group-management-text": "ะ—ะผั–ะฝะธ ะดะพ ั†ั–ั”ั— ะณั€ัƒะฟะธ ะฑัƒะดัƒั‚ัŒ ะฒั–ะดะพะฑั€ะฐะถะตะฝั– ะฝะตะณะฐะนะฝะพ.", "group-id-value": "Id ะณั€ัƒะฟะธ: {0}", "total-households": "ะ’ััŒะพะณะพ ัั–ะผะตะน", - "you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household" + "you-must-select-a-group-before-selecting-a-household": "ะ’ะธ ะผะฐั”ั‚ะต ะฒะธะฑั€ะฐั‚ะธ ะณั€ัƒะฟัƒ ะฟะตั€ะตะด ั‚ะธะผ, ัะบ ะฒะธะฑะธั€ะฐั‚ะธ ัั–ะผ'ัŽ" }, "household": { "household": "ะกั–ะผสผั", @@ -292,9 +292,9 @@ "admin-household-management-text": "ะ—ะผั–ะฝะธ ะดะพ ั†ั–ั”ั— ัั–ะผสผั— ะฑัƒะดัƒั‚ัŒ ะฒั–ะดะพะฑั€ะฐะถะตะฝั– ะฝะตะณะฐะนะฝะพ.", "household-id-value": "ะ†ะดะตะฝั‚ะธั„ั–ะบะฐั‚ะพั€ ัั–ะผสผั—: {0}", "private-household": "ะŸั€ะธะฒะฐั‚ะฝะฐ ัั–ะผสผั", - "private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings", - "lock-recipe-edits-from-other-households": "Lock recipe edits from other households", - "lock-recipe-edits-from-other-households-description": "When enabled only users in your household can edit recipes created by your household", + "private-household-description": "ะฏะบั‰ะพ ะทั€ะพะฑะธั‚ะธ ัั–ะผ'ัŽ ะฟั€ะธะฒะฐั‚ะฝะพัŽ, ั‚ะพ ะฒัั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟัƒะฑะปั–ั‡ะฝะพะณะพ ะฟะตั€ะตะณะปัะดัƒ ะฑัƒะดะต ัะบะธะฝัƒั‚ะพ. ะฆะต ะทะฐะผั–ะฝะธั‚ัŒ ั–ะฝะดะธะฒั–ะดัƒะฐะปัŒะฝั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟัƒะฑะปั–ั‡ะฝะพะณะพ ะฟะตั€ะตะณะปัะดัƒ", + "lock-recipe-edits-from-other-households": "ะ—ะฐะฑะปะพะบัƒะฒะฐั‚ะธ ั€ะตะดะฐะณัƒะฒะฐะฝะฝั ั€ะตั†ะตะฟั‚ั–ะฒ ั–ะฝัˆะธะผะธ ัั–ะผ'ัะผะธ", + "lock-recipe-edits-from-other-households-description": "ะฏะบั‰ะพ ัƒะฒั–ะผะบะฝะตะฝะพ, ั‚ั–ะปัŒะบะธ ั‡ะปะตะฝะธ ะฒะฐัˆะพั— ัั–ะผ'ั— ะทะผะพะถัƒั‚ัŒ ั€ะตะดะฐะณัƒะฒะฐั‚ะธ ั€ะตั†ะตะฟั‚ะธ, ัะบั– ะฑัƒะปะธ ัั‚ะฒะพั€ะตะฝั– ะฒะฐัˆะพัŽ ัั–ะผ'ั”ัŽ", "household-recipe-preferences": "ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ั€ะตั†ะตะฟั‚ั–ะฒ ัั–ะผสผั—", "default-recipe-preferences-description": "ะฆะต ั‚ะธะฟะพะฒั– ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะดะปั ะฝะพะฒะพะณะพ ั€ะตั†ะตะฟั‚ะฐ ัƒ ะฒะฐัˆั–ะน ัั–ะผสผั—. ะฆั– ะฟะฐั€ะฐะผะตั‚ั€ะธ ะผะพะถะฝะฐ ะทะผั–ะฝะธั‚ะธ ะดะปั ะพะบั€ะตะผะธั… ั€ะตั†ะตะฟั‚ั–ะฒ ะฒ ะผะตะฝัŽ ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝัŒ ั€ะตั†ะตะฟั‚ั–ะฒ.", "allow-users-outside-of-your-household-to-see-your-recipes": "ะ”ะพะทะฒะพะปะธั‚ะธ ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผ ะทะฐ ะผะตะถะฐะผะธ ะฒะฐัˆะพั— ัั–ะผสผั— ะฑะฐั‡ะธั‚ะธ ะฒะฐัˆั– ั€ะตั†ะตะฟั‚ะธ", @@ -321,10 +321,10 @@ "mealplan-settings": "ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟะปะฐะฝัƒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "mealplan-update-failed": "ะะต ะฒะดะฐะปะพัั ะพะฝะพะฒะธั‚ะธ ะฟะปะฐะฝ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "mealplan-updated": "ะŸะปะฐะฝ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั ะพะฝะพะฒะปะตะฝะพ", - "mealplan-households-description": "If no household is selected, recipes can be added from any household", + "mealplan-households-description": "ะฏะบั‰ะพ ะถะพะดะฝะพั— ัั–ะผ'ั— ะฝะต ะฒะธะฑั€ะฐะฝะพ, ั€ะตั†ะตะฟั‚ะธ ะผะพะถัƒั‚ัŒ ะฑัƒั‚ะธ ะดะพะดะฐะฝะธะผะธ ะท ะฑัƒะดัŒ-ัะบะพั— ัั–ะผ'ั—", "any-category": "ะ‘ัƒะดัŒ-ัะบะฐ ะบะฐั‚ะตะณะพั€ั–ั", "any-tag": "ะ‘ัƒะดัŒ-ัะบะธะน ั‚ะตะณ", - "any-household": "Any Household", + "any-household": "ะ‘ัƒะดัŒ-ัะบะฐ ัั–ะผ'ั", "no-meal-plan-defined-yet": "ะะต ัั‚ะฒะพั€ะตะฝะพ ะถะพะดะฝะพะณะพ ะฟะปะฐะฝัƒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "no-meal-planned-for-today": "ะะต ะทะฐะฟะปะฐะฝะพะฒะฐะฝะพ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั ะฝะฐ ััŒะพะณะพะดะฝั–", "numberOfDays-hint": "ะกะบั–ะปัŒะบะธ ะดะฝั–ะฒ ะทะฐะฒะฐะฝั‚ะฐะถัƒะฒะฐั‚ะธ ะฝะฐ ัั‚ะพั€ั–ะฝะบัƒ", @@ -357,7 +357,7 @@ "for-type-meal-types": "ะดะปั {0} ั‚ะธะฟั–ะฒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "meal-plan-rules": "ะŸั€ะฐะฒะธะปะฐ ะฟะปะฐะฝั–ะฒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "new-rule": "ะะพะฒะต ะฟั€ะฐะฒะธะปะพ", - "meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.", + "meal-plan-rules-description": "ะ’ะธ ะผะพะถะตั‚ะต ัั‚ะฒะพั€ะธั‚ะธ ะฟั€ะฐะฒะธะปะฐ ะดะปั ะฐะฒั‚ะพะผะฐั‚ะธั‡ะฝะพะณะพ ะฒะธะฑะพั€ัƒ ั€ะตั†ะตะฟั‚ั–ะฒ ะดะปั ะฒะฐัˆะธั… ะฟะปะฐะฝั–ะฒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั. ะฆั– ะฟั€ะฐะฒะธะปะฐ ะฒะธะบะพั€ะธัั‚ะพะฒัƒัŽั‚ัŒัั ัะตั€ะฒะตั€ะพะผ ะดะปั ะฒะธะฑะพั€ัƒ ั€ะตั†ะตะฟั‚ั–ะฒ ะฟั€ะธ ัั‚ะฒะพั€ะตะฝะฝั– ะฟะปะฐะฝัƒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั. ะ—ะฒะตั€ะฝั–ั‚ัŒ ัƒะฒะฐะณัƒ, ั‰ะพ ัะบั‰ะพ ะฟั€ะฐะฒะธะปะฐ ะผะฐัŽั‚ัŒ ะพะฑะผะตะถะตะฝะฝั ะฝะฐ ะดะตะฝัŒ/ั‚ะธะฟ, ั‚ะพ ั—ั… ะบะฐั‚ะตะณะพั€ั–ั— ะฑัƒะดัƒั‚ัŒ ะพะฑ'ั”ะดะฝะฐะฝั–. ะ”ัƒะฑะปัŽะฒะฐั‚ะธ ะฟั€ะฐะฒะธะปะฐ ะฝะตะผะฐั” ัะตะฝััƒ, ะฐะปะต ะผะพะถะปะธะฒะพ.", "new-rule-description": "ะŸั€ะธ ัั‚ะฒะพั€ะตะฝะฝั– ะฝะพะฒะพะณะพ ะฟั€ะฐะฒะธะปะฐ ะดะปั ะฟะปะฐะฝัƒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั, ะฒะธ ะผะพะถะตั‚ะต ะพะฑะผะตะถะธั‚ะธ ะฟั€ะฐะฒะธะปะพ ะฝะฐ ะฟะตะฒะฝะธะน ะดะตะฝัŒ ั‚ะธะถะฝั ั‚ะฐ/ะฐะฑะพ ะฟะตะฒะฝะธะน ั‚ะธะฟ ั—ะถั–. ะฉะพะฑ ะทะฐัั‚ะพััƒะฒะฐั‚ะธ ะฟั€ะฐะฒะธะปะพ ะดะพ ะฒัั–ั… ะดะฝั–ะฒ ะฐะฑะพ ะฒัั–ั… ั‚ะธะฟั–ะฒ ั—ะถั–, ะฒะธ ะผะพะถะตั‚ะต ะฒัั‚ะฐะฝะพะฒะธั‚ะธ ะฟั€ะฐะฒะธะปะพ \"ะ‘ัƒะดัŒ-ัะบะธะน\", ั‰ะพ ะทะฐัั‚ะพัะพะฒัƒะฒะฐั‚ะธะผะต ะนะพะณะพ ะดะพ ะฒัั–ั… ะผะพะถะปะธะฒะธั… ะทะฝะฐั‡ะตะฝัŒ ะดะปั ะดะฝั ั‚ะฐ/ะฐะฑะพ ั‚ะธะฟัƒ ั—ะถั–.", "recipe-rules": "ะŸั€ะฐะฒะธะปะฐ ั€ะตั†ะตะฟั‚ั–ะฒ", "applies-to-all-days": "ะ—ะฐัั‚ะพัะพะฒัƒั”ั‚ัŒัั ะดะพ ะฒัั–ั… ะดะฝั–ะฒ", @@ -518,7 +518,7 @@ "save-recipe-before-use": "ะ—ะฑะตั€ะตะณั‚ะธ ั€ะตั†ะตะฟั‚ ะฟะตั€ะตะด ะฒะธะบะพั€ะธัั‚ะฐะฝะฝัะผ", "section-title": "ะะฐะทะฒะฐ ั€ะพะทะดั–ะปัƒ", "servings": "ะŸะพั€ั†ั–ั—", - "serves-amount": "Serves {amount}", + "serves-amount": "ะŸะพั€ั†ั–ะน: {amount}", "share-recipe-message": "ะฏ ั…ะพั‚ั–ะฒ ะฑะธ ะฟะพะดั–ะปะธั‚ะธัั ะท ั‚ะพะฑะพัŽ ัะฒะพั—ะผ ั€ะตั†ะตะฟั‚ะพะผ {0}.", "show-nutrition-values": "ะŸะพะบะฐะทะฐั‚ะธ ั…ะฐั€ั‡ะพะฒัƒ ั†ั–ะฝะฝั–ัั‚ัŒ", "sodium-content": "ะะฐั‚ั€ั–ะน", @@ -547,8 +547,8 @@ "failed-to-add-recipe-to-mealplan": "ะะต ะฒะดะฐะปะพัั ะดะพะดะฐั‚ะธ ั€ะตั†ะตะฟั‚ ะดะพ ะฟะปะฐะฝัƒ ั…ะฐั€ั‡ัƒะฒะฐะฝะฝั", "failed-to-add-to-list": "ะะต ะฒะดะฐะปะพัั ะดะพะดะฐั‚ะธ ะดะพ ัะฟะธัะบัƒ", "yield": "ะ’ะธั…ั–ะด", - "yields-amount-with-text": "Yields {amount} {text}", - "yield-text": "Yield Text", + "yields-amount-with-text": "ะ’ะธะนะดะต: {amount} {text}", + "yield-text": "ะขะตะบัั‚ ะฒะธั…ะพะดัƒ", "quantity": "ะšั–ะปัŒะบั–ัั‚ัŒ", "choose-unit": "ะ’ะธะฑะตั€ั–ั‚ัŒ ะพะดะธะฝะธั†ั– ะฒะธะผั–ั€ัŽะฒะฐะฝะฝั", "press-enter-to-create": "ะะฐั‚ะธัะฝั–ั‚ัŒ Enter, ั‰ะพะฑ ัั‚ะฒะพั€ะธั‚ะธ", @@ -662,24 +662,24 @@ "no-food": "ะะตะผะฐั” ั—ะถั–" }, "reset-servings-count": "ะกะบะธะฝัƒั‚ะธ ะบั–ะปัŒะบั–ัั‚ัŒ ะฟะพั€ั†ั–ะน", - "not-linked-ingredients": "Additional Ingredients" + "not-linked-ingredients": "ะ”ะพะดะฐั‚ะบะพะฒั– ะฟั€ะพะดัƒะบั‚ะธ" }, "recipe-finder": { - "recipe-finder": "Recipe Finder", - "recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.", - "selected-ingredients": "Selected Ingredients", - "no-ingredients-selected": "No ingredients selected", - "missing": "Missing", - "no-recipes-found": "No recipes found", - "no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters", - "include-ingredients-on-hand": "Include Ingredients On Hand", - "include-tools-on-hand": "Include Tools On Hand", - "max-missing-ingredients": "Max Missing Ingredients", - "max-missing-tools": "Max Missing Tools", - "selected-tools": "Selected Tools", - "other-filters": "Other Filters", - "ready-to-make": "Ready to Make", - "almost-ready-to-make": "Almost Ready to Make" + "recipe-finder": "ะจัƒะบะฐั‡ ั€ะตั†ะตะฟั‚ั–ะฒ", + "recipe-finder-description": "ะŸะพัˆัƒะบ ั€ะตั†ะตะฟั‚ั–ะฒ ะฑะฐะทัƒั”ั‚ัŒัั ะฝะฐ ะฟั€ะพะดัƒะบั‚ะฐั…, ัะบั– ะฒะธ ะผะฐั”ั‚ะต. ะ’ะธ ั‚ะฐะบะพะถ ะผะพะถะตั‚ะต ั„ั–ะปัŒั‚ั€ัƒะฒะฐั‚ะธ ะทะฐ ะฝะฐัะฒะฝะธะผะธ ั–ะฝัั‚ั€ัƒะผะตะฝั‚ะฐะผะธ ั‚ะฐ ะฒัั‚ะฐะฝะพะฒะธั‚ะธ ะผะฐะบัะธะผะฐะปัŒะฝัƒ ะบั–ะปัŒะบั–ัั‚ัŒ ะฒั–ะดััƒั‚ะฝั–ั… ะฟั€ะพะดัƒะบั‚ั–ะฒ ะฐะฑะพ ั–ะฝัั‚ั€ัƒะผะตะฝั‚ั–ะฒ.", + "selected-ingredients": "ะ’ะธะฑั€ะฐะฝั– ะฟั€ะพะดัƒะบั‚ะธ", + "no-ingredients-selected": "ะ–ะพะดะฝะพะณะพ ะฟั€ะพะดัƒะบั‚ัƒ ะฝะต ะฒะธะฑั€ะฐะฝะพ", + "missing": "ะ’ั–ะดััƒั‚ะฝั–", + "no-recipes-found": "ะ ะตั†ะตะฟั‚ั–ะฒ ะฝะต ะทะฝะฐะนะดะตะฝะพ", + "no-recipes-found-description": "ะกะฟั€ะพะฑัƒะนั‚ะต ะดะพะดะฐั‚ะธ ะฑั–ะปัŒัˆะต ะฟั€ะพะดัƒะบั‚ั–ะฒ ะดะพ ะฟะพัˆัƒะบะพะฒะพะณะพ ัะฟะธัะบัƒ ะฐะฑะพ ะฟั–ะดะปะฐัˆั‚ัƒะฒะฐั‚ะธ ั„ั–ะปัŒั‚ั€ะธ", + "include-ingredients-on-hand": "ะ’ะบะปัŽั‡ะธั‚ะธ ะฝะฐัะฒะฝั– ะฟั€ะพะดัƒะบั‚ะธ", + "include-tools-on-hand": "ะ’ะบะปัŽั‡ะธั‚ะธ ะฝะฐัะฒะฝั– ั–ะฝัั‚ั€ัƒะผะตะฝั‚ะธ", + "max-missing-ingredients": "ะœะฐะบัะธะผัƒะผ ะฒั–ะดััƒั‚ะฝั–ั… ะฟั€ะพะดัƒะบั‚ั–ะฒ", + "max-missing-tools": "ะœะฐะบัะธะผัƒะผ ะฒั–ะดััƒั‚ะฝั–ั… ั–ะฝัั‚ั€ัƒะผะตะฝั‚ั–ะฒ", + "selected-tools": "ะ’ะธะฑั€ะฐะฝั– ั–ะฝัั‚ั€ัƒะผะตะฝั‚ะธ", + "other-filters": "ะ†ะฝัˆั– ั„ั–ะปัŒั‚ั€ะธ", + "ready-to-make": "ะ“ะพั‚ะพะฒะต ะดะพ ะฟั€ะธะณะพั‚ัƒะฒะฐะฝะฝั", + "almost-ready-to-make": "ะœะฐะนะถะต ะณะพั‚ะพะฒะต ะดะพ ะฟั€ะธะณะพั‚ัƒะฒะฐะฝะฝั" }, "search": { "advanced-search": "ะ ะพะทัˆะธั€ะตะฝะธะน ะฟะพัˆัƒะบ", @@ -884,7 +884,7 @@ "are-you-sure-you-want-to-check-all-items": "ะ’ะธ ะฒะฟะตะฒะฝะตะฝั–, ั‰ะพ ั…ะพั‡ะตั‚ะต ะฒั–ะดะผั–ั‚ะธั‚ะธ ะฒัั– ะตะปะตะผะตะฝั‚ะธ?", "are-you-sure-you-want-to-uncheck-all-items": "ะ’ะธ ะฒะฟะตะฒะฝะตะฝั–, ั‰ะพ ั…ะพั‡ะตั‚ะต ะทะฝัั‚ะธ ะฒั–ะดะผั–ั‚ะบัƒ ะท ัƒัั–ั… ะตะปะตะผะตะฝั‚ั–ะฒ?", "are-you-sure-you-want-to-delete-checked-items": "ะ’ะธ ะฒะฟะตะฒะฝะตะฝั–, ั‰ะพ ั…ะพั‡ะตั‚ะต ะฒะธะดะฐะปะธั‚ะธ ะฒัั– ะฒั–ะดะผั–ั‡ะตะฝั– ะตะปะตะผะตะฝั‚ะธ?", - "no-shopping-lists-found": "No Shopping Lists Found" + "no-shopping-lists-found": "ะกะฟะธัะบั–ะฒ ะฟะพะบัƒะฟะพะบ ะฝะต ะทะฝะฐะนะดะตะฝะพ" }, "sidebar": { "all-recipes": "ะ’ัั– ั€ะตั†ะตะฟั‚ะธ", @@ -1030,7 +1030,7 @@ "administrator": "ะะดะผั–ะฝั–ัั‚ั€ะฐั‚ะพั€", "user-can-invite-other-to-group": "ะšะพั€ะธัั‚ัƒะฒะฐั‡ ะผะพะถะต ะทะฐะฟั€ะพัˆัƒะฒะฐั‚ะธ ั–ะฝัˆะธั… ะฒ ะณั€ัƒะฟัƒ", "user-can-manage-group": "ะšะพั€ะธัั‚ัƒะฒะฐั‡ ะผะพะถะต ะบะตั€ัƒะฒะฐั‚ะธ ะณั€ัƒะฟะพัŽ", - "user-can-manage-household": "User can manage household", + "user-can-manage-household": "ะšะพั€ะธัั‚ัƒะฒะฐั‡ ะผะพะถะต ัƒะฟั€ะฐะฒะปัั‚ะธ ัั–ะผ'ั”ัŽ", "user-can-organize-group-data": "ะšะพั€ะธัั‚ัƒะฒะฐั‡ ะผะพะถะต ะฒะฟะพั€ัะดะบะพะฒัƒะฒะฐั‚ะธ ะดะฐะฝั– ะณั€ัƒะฟะธ", "enable-advanced-features": "ะฃะฒั–ะผะบะฝัƒั‚ะธ ะดะพะดะฐั‚ะบะพะฒั– ั„ัƒะฝะบั†ั–ั—", "it-looks-like-this-is-your-first-time-logging-in": "ะกั…ะพะถะต, ะฒะธ ะทะฐั…ะพะดะธั‚ะต ะฒะฟะตั€ัˆะต.", @@ -1290,13 +1290,13 @@ "debug-openai-services-description": "ะ’ะธะบะพั€ะธัั‚ะพะฒัƒะนั‚ะต ั†ัŽ ัั‚ะพั€ั–ะฝะบัƒ, ั‰ะพะฑ ะฝะฐะปะฐะณะพะดะธั‚ะธ ัะปัƒะถะฑะธ OpenAI. ะ’ะธ ะผะพะถะตั‚ะต ะฟะตั€ะตะฒั–ั€ะธั‚ะธ ะฒะฐัˆะต ะท'ั”ะดะฝะฐะฝะฝั ะท OpenAI ะน ะฟะพะฑะฐั‡ะธั‚ะธ ั€ะตะทัƒะปัŒั‚ะฐั‚ะธ ั‚ัƒั‚. ะฏะบั‰ะพ ะฒะฒั–ะผะบะฝะตะฝะพ ัะปัƒะถะฑะธ ะทะพะฑั€ะฐะถะตะฝัŒ, ะฒะธ ั‚ะฐะบะพะถ ะผะพะถะตั‚ะต ะฝะฐะดะฐั‚ะธ ะทะพะฑั€ะฐะถะตะฝะฝั.", "run-test": "ะ—ะฐะฟัƒัั‚ะธั‚ะธ ะฟะตั€ะตะฒั–ั€ะบัƒ", "test-results": "ะ ะตะทัƒะปัŒั‚ะฐั‚ะธ ะฟะตั€ะตะฒั–ั€ะบะธ", - "group-delete-note": "Groups with users or households cannot be deleted", - "household-delete-note": "Households with users cannot be deleted" + "group-delete-note": "ะะต ะผะพะถะฝะฐ ะฒะธะดะฐะปะธั‚ะธ ะณั€ัƒะฟะธ ะท ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผะธ ั‡ะธ ัั–ะผ'ัะผะธ ะฒ ะฝั–ะน", + "household-delete-note": "ะะต ะผะพะถะฝะฐ ะฒะธะดะฐะปะธั‚ะธ ัั–ะผ'ัŽ ะท ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐะผะธ ะฒ ะฝั–ะน" }, "profile": { "welcome-user": "๐Ÿ‘‹ ะ›ะฐัะบะฐะฒะพ ะฟั€ะพัะธะผะพ, {0}!", "description": "ะšะตั€ัƒะฒะฐะฝะฝั ะฒะฐัˆะธะผ ะฟั€ะพั„ั–ะปะตะผ, ั€ะตั†ะตะฟั‚ะฐะผะธ ั‚ะฐ ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝัะผะธ ะณั€ัƒะฟะธ.", - "invite-link": "Invite Link", + "invite-link": "ะŸะพัะธะปะฐะฝะฝั-ะทะฐะฟั€ะพัˆะตะฝะฝั", "get-invite-link": "ะžั‚ั€ะธะผะฐั‚ะธ ะฟะพัะธะปะฐะฝะฝั-ะทะฐะฟั€ะพัˆะตะฝะฝั", "get-public-link": "ะžั‚ั€ะธะผะฐั‚ะธ ะฟัƒะฑะปั–ั‡ะฝะต ะฟะพัะธะปะฐะฝะฝั", "account-summary": "ะะบะบะฐัƒะฝั‚", @@ -1345,9 +1345,9 @@ }, "cookbook": { "cookbooks": "ะšัƒะปั–ะฝะฐั€ะฝั– ะบะฝะธะณะธ", - "description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.", - "hide-cookbooks-from-other-households": "Hide Cookbooks from Other Households", - "hide-cookbooks-from-other-households-description": "When enabled, only cookbooks from your household will appear on the sidebar", + "description": "ะšัƒะปั–ะฝะฐั€ะฝั– ะบะฝะธะณะธ - ั†ะต ั‰ะต ะพะดะธะฝ ัะฟะพัั–ะฑ ะพั€ะณะฐะฝั–ะทะพะฒัƒะฒะฐั‚ะธ ั€ะตั†ะตะฟั‚ะธ ะทะฐ ะดะพะฟะพะผะพะณะพัŽ ั€ะพะทะดั–ะปั–ะฒ ั‚ะฐ ั–ะฝัˆะธั… ั„ั–ะปัŒั‚ั€ั–ะฒ. ะะพะฒะฐ ะบัƒะปั–ะฝะฐั€ะฝะฐ ะบะฝะธะณะฐ ะท'ัะฒะธั‚ัŒัั ะฝะฐ ะฑะพะบะพะฒั–ะน ะฟะฐะฝะตะปั–, ั– ะฒัั– ั€ะตั†ะตะฟั‚ะธ, ัะบั– ะฒั–ะดะฟะพะฒั–ะดะฐัŽั‚ัŒ ะพะฑั€ะฐะฝะธะผ ั„ั–ะปัŒั‚ั€ะฐะผ, ะฑัƒะดัƒั‚ัŒ ะฟะพะบะฐะทัƒะฒะฐั‚ะธัั ะฒ ะบัƒะปั–ะฝะฐั€ะฝั–ะน ะบะฝะธะทั–.", + "hide-cookbooks-from-other-households": "ะŸั€ะธั…ะพะฒะฐั‚ะธ ะบัƒะปั–ะฝะฐั€ะฝั– ะบะฝะธะณะธ ะฒั–ะด ั–ะฝัˆะธั… ัั–ะผะตะน", + "hide-cookbooks-from-other-households-description": "ะฏะบั‰ะพ ะฒะธะฑั€ะฐะฝะพ, ั‚ั–ะปัŒะบะธ ะบัƒะปั–ะฝะฐั€ะฝั– ะบะฝะธะณะธ ะฒะฐัˆะพั— ัั–ะผ'ั— ะฑัƒะดะต ะฒะธะดะฝะพ ะฝะฐ ะฑะพะบะพะฒั–ะน ะฟะฐะฝะตะปั–", "public-cookbook": "ะŸัƒะฑะปั–ั‡ะฝะฐ ะบัƒะปั–ะฝะฐั€ะฝะฐ ะบะฝะธะณะฐ", "public-cookbook-description": "ะŸัƒะฑะปั–ั‡ะฝะธะผะธ ะบัƒะปั–ะฝะฐั€ะฝะธะผะธ ะบะฝะธะณะฐะผะธ ะผะพะถะฝะฐ ะฟะพะดั–ะปะธั‚ะธัั ะท ะฑัƒะดัŒ-ะบะธะผ, ั– ะฒะพะฝะธ ะฑัƒะดัƒั‚ัŒ ะฒั–ะดะพะฑั€ะฐะถะฐั‚ะธัั ะฝะฐ ัั‚ะพั€ั–ะฝั†ั– ะฒะฐัˆะพั— ะณั€ัƒะฟะธ.", "filter-options": "ะŸะฐั€ะฐะผะตั‚ั€ะธ ั„ั–ะปัŒั‚ั€ะฐ", diff --git a/frontend/package.json b/frontend/package.json index 1e7ba0582..0fe13538b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mealie", -"version": "2.5.0", +"version": "2.6.0", "private": true, "scripts": { "dev": "nuxt", diff --git a/frontend/pages/shopping-lists/_id.vue b/frontend/pages/shopping-lists/_id.vue index c575f0de3..4b905c3a7 100644 --- a/frontend/pages/shopping-lists/_id.vue +++ b/frontend/pages/shopping-lists/_id.vue @@ -151,6 +151,24 @@ + +
+ +
+
+ {{ $t('general.add') }} +
+ - -
- -
-
- {{ $t('general.add') }} -
-
diff --git a/mealie/alembic/versions/2025-02-09-15.31.00_7cf3054cbbcc_remove_instructions_index.py b/mealie/alembic/versions/2025-02-09-15.31.00_7cf3054cbbcc_remove_instructions_index.py new file mode 100644 index 000000000..f6fbcca9d --- /dev/null +++ b/mealie/alembic/versions/2025-02-09-15.31.00_7cf3054cbbcc_remove_instructions_index.py @@ -0,0 +1,146 @@ +"""remove instructions index + +Revision ID: 7cf3054cbbcc +Revises: b9e516e2d3b3 +Create Date: 2025-02-09 15:31:00.772295 + +""" + +import sqlalchemy as sa +from sqlalchemy import orm +from alembic import op +from mealie.db.models._model_utils.guid import GUID +from mealie.core.root_logger import get_logger + +# revision identifiers, used by Alembic. +revision = "7cf3054cbbcc" +down_revision: str | None = "b9e516e2d3b3" +branch_labels: str | tuple[str, ...] | None = None +depends_on: str | tuple[str, ...] | None = None + +logger = get_logger() + + +class SqlAlchemyBase(orm.DeclarativeBase): + @classmethod + def normalized_fields(cls) -> list[orm.InstrumentedAttribute]: + return [] + + +class RecipeModel(SqlAlchemyBase): + __tablename__ = "recipes" + + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + name_normalized: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False, index=True) + description_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [cls.name_normalized, cls.description_normalized] + + +class RecipeIngredientModel(SqlAlchemyBase): + __tablename__ = "recipes_ingredients" + + id: orm.Mapped[int] = orm.mapped_column(sa.Integer, primary_key=True) + note_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + original_text_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [cls.note_normalized, cls.original_text_normalized] + + +class IngredientFoodModel(SqlAlchemyBase): + __tablename__ = "ingredient_foods" + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + plural_name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [cls.name_normalized, cls.plural_name_normalized] + + +class IngredientFoodAliasModel(SqlAlchemyBase): + __tablename__ = "ingredient_foods_aliases" + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [cls.name_normalized] + + +class IngredientUnitModel(SqlAlchemyBase): + __tablename__ = "ingredient_units" + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + plural_name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + abbreviation_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + plural_abbreviation_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [ + cls.name_normalized, + cls.plural_name_normalized, + cls.abbreviation_normalized, + cls.plural_abbreviation_normalized, + ] + + +class IngredientUnitAliasModel(SqlAlchemyBase): + __tablename__ = "ingredient_units_aliases" + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True) + + @classmethod + def normalized_fields(cls): + return [cls.name_normalized] + + +def truncate_normalized_fields() -> None: + bind = op.get_bind() + session = orm.Session(bind=bind) + + models: list[type[SqlAlchemyBase]] = [ + RecipeModel, + RecipeIngredientModel, + IngredientFoodModel, + IngredientFoodAliasModel, + IngredientUnitModel, + IngredientUnitAliasModel, + ] + + for model in models: + for record in session.query(model).all(): + for field in model.normalized_fields(): + if not (field_value := getattr(record, field.key)): + continue + + setattr(record, field.key, field_value[:255]) + + try: + session.commit() + except Exception: + logger.exception(f"Failed to truncate normalized fields for {model.__name__}") + session.rollback() + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("recipe_instructions", schema=None) as batch_op: + batch_op.drop_index("ix_recipe_instructions_text") + + # ### end Alembic commands ### + + truncate_normalized_fields() + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("recipe_instructions", schema=None) as batch_op: + batch_op.create_index("ix_recipe_instructions_text", ["text"], unique=False) + + # ### end Alembic commands ### diff --git a/mealie/app.py b/mealie/app.py index 1debfa068..8ea6811a6 100644 --- a/mealie/app.py +++ b/mealie/app.py @@ -1,3 +1,12 @@ +import re +import warnings + +# pyrdfa3 is no longer being updated and has docstrings that emit syntax warnings +warnings.filterwarnings( + "ignore", module=".*pyRdfa", category=SyntaxWarning, message=re.escape("invalid escape sequence '\\-'") +) + +# ruff: noqa: E402 from collections.abc import AsyncGenerator from contextlib import asynccontextmanager @@ -115,6 +124,7 @@ register_debug_handler(app) async def start_scheduler(): SchedulerRegistry.register_daily( + tasks.purge_expired_tokens, tasks.purge_group_registration, tasks.purge_password_reset_tokens, tasks.purge_group_data_exports, diff --git a/mealie/core/settings/settings.py b/mealie/core/settings/settings.py index 7558a29c5..e89b2e631 100644 --- a/mealie/core/settings/settings.py +++ b/mealie/core/settings/settings.py @@ -12,6 +12,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict from mealie.core.settings.themes import Theme from .db_providers import AbstractDBProvider, db_provider_factory +from .static import PACKAGE_DIR class ScheduleTime(NamedTuple): @@ -109,7 +110,7 @@ class AppSettings(AppLoggingSettings): BASE_URL: str = "http://localhost:8080" """trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)""" - STATIC_FILES: str = "" + STATIC_FILES: str = str(PACKAGE_DIR / "frontend") """path to static files directory (ex. `mealie/dist`)""" IS_DEMO: bool = False diff --git a/mealie/core/settings/static.py b/mealie/core/settings/static.py index f80642849..1a5af2097 100644 --- a/mealie/core/settings/static.py +++ b/mealie/core/settings/static.py @@ -5,4 +5,5 @@ from mealie import __version__ APP_VERSION = __version__ CWD = Path(__file__).parent +PACKAGE_DIR = CWD.parent.parent BASE_DIR = CWD.parent.parent.parent diff --git a/mealie/db/init_db.py b/mealie/db/init_db.py index 75950a5a2..aab65bca6 100644 --- a/mealie/db/init_db.py +++ b/mealie/db/init_db.py @@ -69,16 +69,16 @@ def db_is_at_head(alembic_cfg: config.Config) -> bool: def safe_try(func: Callable): try: func() - except Exception as e: - logger.error(f"Error calling '{func.__name__}': {e}") + except Exception: + logger.exception(f"Error calling '{func.__name__}'") def connect(session: orm.Session) -> bool: try: session.execute(text("SELECT 1")) return True - except Exception as e: - logger.error(f"Error connecting to database: {e}") + except Exception: + logger.exception("Error connecting to database") return False @@ -106,23 +106,27 @@ def main(): if not os.path.isfile(alembic_cfg_path): raise Exception("Provided alembic config path doesn't exist") + run_fixes = False alembic_cfg = Config(alembic_cfg_path) if db_is_at_head(alembic_cfg): logger.debug("Migration not needed.") else: logger.info("Migration needed. Performing migration...") command.upgrade(alembic_cfg, "head") + run_fixes = True if session.get_bind().name == "postgresql": # needed for fuzzy search and fast GIN text indices session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm;")) db = get_repositories(session, group_id=None, household_id=None) - safe_try(lambda: fix_migration_data(session)) - safe_try(lambda: fix_slug_food_names(db)) - safe_try(lambda: fix_group_with_no_name(session)) if db.users.get_all(): logger.debug("Database exists") + if run_fixes: + safe_try(lambda: fix_migration_data(session)) + safe_try(lambda: fix_slug_food_names(db)) + safe_try(lambda: fix_group_with_no_name(session)) + else: logger.info("Database contains no users, initializing...") init_db(session) diff --git a/mealie/db/models/_model_base.py b/mealie/db/models/_model_base.py index fd5e68c80..4298a838b 100644 --- a/mealie/db/models/_model_base.py +++ b/mealie/db/models/_model_base.py @@ -18,7 +18,9 @@ class SqlAlchemyBase(DeclarativeBase): @classmethod def normalize(cls, val: str) -> str: - return unidecode(val).lower().strip() + # We cap the length to 255 to prevent indexes from being too long; see: + # https://www.postgresql.org/docs/current/btree.html + return unidecode(val).lower().strip()[:255] class BaseMixins: diff --git a/mealie/db/models/recipe/instruction.py b/mealie/db/models/recipe/instruction.py index 70f7253ec..395b5dba9 100644 --- a/mealie/db/models/recipe/instruction.py +++ b/mealie/db/models/recipe/instruction.py @@ -23,8 +23,8 @@ class RecipeInstruction(SqlAlchemyBase): recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True) position: Mapped[int | None] = mapped_column(Integer, index=True) type: Mapped[str | None] = mapped_column(String, default="") - title: Mapped[str | None] = mapped_column(String) # This is the section title!!! - text: Mapped[str | None] = mapped_column(String, index=True) + title: Mapped[str | None] = mapped_column(String) # This is the section title + text: Mapped[str | None] = mapped_column(String) summary: Mapped[str | None] = mapped_column(String) ingredient_references: Mapped[list[RecipeIngredientRefLink]] = orm.relationship( diff --git a/mealie/lang/messages/gl-ES.json b/mealie/lang/messages/gl-ES.json index e5d8c10ce..549b99a42 100644 --- a/mealie/lang/messages/gl-ES.json +++ b/mealie/lang/messages/gl-ES.json @@ -1,13 +1,13 @@ { "generic": { - "server-error": "An unexpected error occurred" + "server-error": "Ocorreu un erro inesperado" }, "recipe": { - "unique-name-error": "Recipe names must be unique", - "recipe-created": "Recipe Created", + "unique-name-error": "Os nomes de receitas deven ser รบnicos", + "recipe-created": "Receita creada", "recipe-defaults": { "ingredient-note": "1 Cup Flour", - "step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n" + "step-text": "Os pasos da receita, como outros campos na pรกxina da receita, suportan a sintaxe markdown.\n\n**Adicionar un link**\n\n[Meu link](https://demo.mealie.io)\n" }, "servings-text": { "makes": "Makes", @@ -33,26 +33,26 @@ "exceptions": { "permission_denied": "You do not have permission to perform this action", "no-entry-found": "The requested resource was not found", - "integrity-error": "Database integrity error", - "username-conflict-error": "This username is already taken", + "integrity-error": "Erro de integridade da base de datos", + "username-conflict-error": "Este nome de usuario xa estรก en uso", "email-conflict-error": "This email is already in use" }, "notifications": { - "generic-created": "{name} was created", - "generic-updated": "{name} was updated", - "generic-created-with-url": "{name} has been created, {url}", - "generic-updated-with-url": "{name} has been updated, {url}", - "generic-duplicated": "{name} has been duplicated", + "generic-created": "{name} creado", + "generic-updated": "{name} atualizado", + "generic-created-with-url": "{name} foi creado, {url}", + "generic-updated-with-url": "{name} foi atualizado, {url}", + "generic-duplicated": "{name} foi duplicado", "generic-deleted": "{name} has been deleted" }, "datetime": { - "year": "year|years", - "day": "day|days", - "hour": "hour|hours", - "minute": "minute|minutes", - "second": "second|seconds", - "millisecond": "millisecond|milliseconds", - "microsecond": "microsecond|microseconds" + "year": "ano|anos", + "day": "dia|dias", + "hour": "hora|horas", + "minute": "minuto|minutos", + "second": "segundo|segundos", + "millisecond": "milisegundo|milisegundos", + "microsecond": "microsegundo|microsegundos" }, "emails": { "password": { @@ -67,14 +67,14 @@ "header_text": "You're Invited!", "message_top": "You have been invited to join Mealie.", "message_bottom": "Please click the button above to accept the invitation.", - "button_text": "Accept Invitation" + "button_text": "Aceptar Convite" }, "test": { "subject": "Mealie Test Email", "header_text": "Test Email", "message_top": "This is a test email.", "message_bottom": "Please click the button above to test the email.", - "button_text": "Open Mealie" + "button_text": "Abrir o Mealie" } } } diff --git a/mealie/lang/messages/pt-BR.json b/mealie/lang/messages/pt-BR.json index 904fb6640..3b2cd8254 100644 --- a/mealie/lang/messages/pt-BR.json +++ b/mealie/lang/messages/pt-BR.json @@ -4,7 +4,7 @@ }, "recipe": { "unique-name-error": "Nomes de receitas devem ser รบnicos", - "recipe-created": "Recipe Created", + "recipe-created": "Receita criada", "recipe-defaults": { "ingredient-note": "1 Xรญcara de Farinha", "step-text": "Passos das receitas, assim como outros campos na pรกgina da receita, suportam sintaxe \"markdown\".\n\n**Adicione um link**\n\n[Meu Link](https://demo.mealie.io)\n" @@ -12,8 +12,8 @@ "servings-text": { "makes": "Makes", "serves": "Serves", - "serving": "Serving", - "servings": "Servings", + "serving": "Porรงรฃo", + "servings": "Porรงรตes", "yield": "Yield", "yields": "Yields" } diff --git a/mealie/lang/messages/uk-UA.json b/mealie/lang/messages/uk-UA.json index da56f1583..8b6b23cce 100644 --- a/mealie/lang/messages/uk-UA.json +++ b/mealie/lang/messages/uk-UA.json @@ -4,16 +4,16 @@ }, "recipe": { "unique-name-error": "ะะฐะทะฒะฐ ั€ะตั†ะตะฟั‚ัƒ ะฟะพะฒะธะฝะฝะฐ ะฑัƒั‚ะธ ัƒะฝั–ะบะฐะปัŒะฝะพัŽ", - "recipe-created": "Recipe Created", + "recipe-created": "ะ ะตั†ะตะฟั‚ ัั‚ะฒะพั€ะตะฝะพ", "recipe-defaults": { "ingredient-note": "ะกั‚ะฐะบะฐะฝ ะฑะพั€ะพัˆะฝะฐ", - "step-text": "ะšั€ะพะบะธ ั€ะตั†ะตะฟั‚ั–ะฒ, ั‚ะฐะบ ัะฐะผะพ ัะบ ั– ั–ะฝัˆั– ะฟะพะปั ัั‚ะพั€ั–ะฝะบะธ, ะฟั–ะดั‚ั€ะธะผัƒัŽั‚ัŒ ัะธะฝั‚ะฐะบัะธั markdown.\n\n**ะ”ะพะดะฐั‚ะธ ะฟะพัะธะปะฐะฝะฝั**\n\n[Mะพั” ะฟะพัะธะปะฐะฝะฝั](https://demo.mealie.io)\n" + "step-text": "ะšั€ะพะบะธ ั€ะตั†ะตะฟั‚ั–ะฒ, ั‚ะฐะบ ัะฐะผะพ ัะบ ั– ั–ะฝัˆั– ะฟะพะปั ัั‚ะพั€ั–ะฝะบะธ, ะฟั–ะดั‚ั€ะธะผัƒัŽั‚ัŒ ัะธะฝั‚ะฐะบัะธั markdown.\n\n**ะ”ะพะดะฐั‚ะธ ะฟะพัะธะปะฐะฝะฝั**\n\n[ะœะพั” ะฟะพัะธะปะฐะฝะฝั](https://demo.mealie.io)\n" }, "servings-text": { "makes": "Makes", "serves": "Serves", - "serving": "Serving", - "servings": "Servings", + "serving": "ะŸะพั€ั†ั–ั", + "servings": "ะŸะพั€ั†ั–ั—", "yield": "Yield", "yields": "Yields" } diff --git a/mealie/main.py b/mealie/main.py index d85d2de9f..bd4414d30 100644 --- a/mealie/main.py +++ b/mealie/main.py @@ -6,7 +6,7 @@ from mealie.core.logger.config import log_config def main(): uvicorn.run( - "app:app", + "mealie.app:app", host=settings.API_HOST, port=settings.API_PORT, log_level=settings.LOG_LEVEL.lower(), diff --git a/mealie/repos/seed/resources/foods/locales/fr-FR.json b/mealie/repos/seed/resources/foods/locales/fr-FR.json index 014bb9fe3..4af4bf56a 100644 --- a/mealie/repos/seed/resources/foods/locales/fr-FR.json +++ b/mealie/repos/seed/resources/foods/locales/fr-FR.json @@ -1,6 +1,6 @@ { "acorn-squash": { - "name": "courgeron" + "name": "courge poivrรฉe" }, "alfalfa-sprouts": { "name": "pousses de luzerne" @@ -109,7 +109,7 @@ "name": "cannabis" }, "capsicum": { - "name": "capsicum" + "name": "piment" }, "caraway": { "name": "cumin" diff --git a/mealie/repos/seed/resources/foods/locales/gl-ES.json b/mealie/repos/seed/resources/foods/locales/gl-ES.json index 70054f8a0..e74d4b336 100644 --- a/mealie/repos/seed/resources/foods/locales/gl-ES.json +++ b/mealie/repos/seed/resources/foods/locales/gl-ES.json @@ -6,20 +6,20 @@ "name": "alfalfa sprouts" }, "anchovies": { - "name": "anchovies" + "name": "anchoas" }, "apples": { - "name": "apple", - "plural_name": "apples" + "name": "mazรก", + "plural_name": "mazรกs" }, "artichoke": { - "name": "artichoke" + "name": "alcachofa" }, "arugula": { - "name": "arugula" + "name": "rรบcula" }, "asparagus": { - "name": "asparagus" + "name": "espรกrragos" }, "avocado": { "name": "avocado", @@ -29,7 +29,7 @@ "name": "bacon" }, "baking-powder": { - "name": "baking powder" + "name": "fermento en po" }, "baking-soda": { "name": "baking soda" @@ -51,7 +51,7 @@ "plural_name": "bell peppers" }, "blackberries": { - "name": "blackberries" + "name": "amoras" }, "bok-choy": { "name": "bok choy" @@ -60,7 +60,7 @@ "name": "brassicas" }, "bread": { - "name": "bread" + "name": "pan" }, "breadfruit": { "name": "breadfruit" @@ -84,7 +84,7 @@ "name": "brussels sprouts" }, "butter": { - "name": "butter" + "name": "manteiga" }, "butternut-pumpkin": { "name": "butternut pumpkin" @@ -93,8 +93,8 @@ "name": "butternut squash" }, "cabbage": { - "name": "cabbage", - "plural_name": "cabbages" + "name": "repolo", + "plural_name": "repolos" }, "cactus-edible": { "name": "cactus, edible" @@ -115,8 +115,8 @@ "name": "caraway" }, "carrot": { - "name": "carrot", - "plural_name": "carrots" + "name": "cenoura", + "plural_name": "cenouras" }, "caster-sugar": { "name": "caster sugar" @@ -144,7 +144,7 @@ "name": "cereal grains" }, "chard": { - "name": "chard" + "name": "acelga" }, "cheese": { "name": "queixo" @@ -176,7 +176,7 @@ }, "coconut": { "name": "coco", - "plural_name": "coconuts" + "plural_name": "cocos" }, "coconut-milk": { "name": "leite de coco" @@ -194,7 +194,7 @@ "name": "confectioners' sugar" }, "coriander": { - "name": "coriander" + "name": "coentro" }, "corn": { "name": "millo", @@ -213,7 +213,7 @@ "name": "cream of tartar" }, "cucumber": { - "name": "pepino", + "name": "cogombro", "plural_name": "cucumbers" }, "cumin": { @@ -243,8 +243,8 @@ "plural_name": "eggplants" }, "eggs": { - "name": "ovos", - "plural_name": "eggs" + "name": "ovo", + "plural_name": "ovos" }, "endive": { "name": "endive", @@ -291,14 +291,14 @@ "name": "garam masala" }, "garlic": { - "name": "garlic", - "plural_name": "garlics" + "name": "allo", + "plural_name": "allos" }, "gem-squash": { "name": "gem squash" }, "ghee": { - "name": "ghee" + "name": "manteiga ghee" }, "giblets": { "name": "giblets" @@ -330,7 +330,7 @@ "name": "herbs" }, "honey": { - "name": "honey" + "name": "mel" }, "isomalt": { "name": "isomalt" @@ -383,8 +383,8 @@ "name": "lettuce" }, "liver": { - "name": "liver", - "plural_name": "livers" + "name": "fรญgado", + "plural_name": "figados" }, "maize": { "name": "maize" @@ -393,20 +393,20 @@ "name": "maple syrup" }, "meat": { - "name": "meat" + "name": "carne" }, "milk": { "name": "leite" }, "mortadella": { - "name": "mortadella" + "name": "mortadela" }, "mushroom": { - "name": "mushroom", - "plural_name": "mushrooms" + "name": "cogumelo", + "plural_name": "cogumelos" }, "mussels": { - "name": "mussels" + "name": "mexillรณn" }, "nanaimo-bar-mix": { "name": "nanaimo bar mix" @@ -424,8 +424,8 @@ "name": "nuts" }, "octopuses": { - "name": "octopus", - "plural_name": "octopuses" + "name": "polbo", + "plural_name": "polbos" }, "oils": { "name": "oils" @@ -440,7 +440,7 @@ "name": "olive oil" }, "onion": { - "name": "onion" + "name": "cebola" }, "onion-family": { "name": "onion family" @@ -450,13 +450,13 @@ }, "oranges": { "name": "laranxas", - "plural_name": "oranges" + "plural_name": "laranxas" }, "oregano": { "name": "oregano" }, "oysters": { - "name": "oysters" + "name": "ostras" }, "panch-puran": { "name": "panch puran" @@ -473,13 +473,13 @@ }, "pear": { "name": "pera", - "plural_name": "pears" + "plural_name": "peras" }, "peas": { "name": "peas" }, "pepper": { - "name": "pepper", + "name": "", "plural_name": "peppers" }, "pineapple": { @@ -491,11 +491,11 @@ "plural_name": "plantains" }, "poppy-seeds": { - "name": "poppy seeds" + "name": "sementes de papoula" }, "potato": { - "name": "potato", - "plural_name": "potatoes" + "name": "pataca", + "plural_name": "patacas" }, "poultry": { "name": "poultry" @@ -524,7 +524,7 @@ "name": "arroz" }, "rice-flour": { - "name": "rice flour" + "name": "fariรฑa de arroz" }, "rock-sugar": { "name": "rock sugar" @@ -536,7 +536,7 @@ "name": "salmรณn" }, "salt": { - "name": "salt" + "name": "sal" }, "salt-cod": { "name": "bacallau en salgadura" @@ -546,10 +546,10 @@ "plural_name": "scallions" }, "seafood": { - "name": "seafood" + "name": "marisco" }, "seeds": { - "name": "seeds" + "name": "sementes" }, "sesame-seeds": { "name": "sesame seeds" @@ -633,7 +633,7 @@ }, "tomato": { "name": "tomate", - "plural_name": "tomatoes" + "plural_name": "tomates" }, "trout": { "name": "troita" diff --git a/mealie/repos/seed/resources/foods/locales/pt-BR.json b/mealie/repos/seed/resources/foods/locales/pt-BR.json index 63bf315be..726e25177 100644 --- a/mealie/repos/seed/resources/foods/locales/pt-BR.json +++ b/mealie/repos/seed/resources/foods/locales/pt-BR.json @@ -10,7 +10,7 @@ }, "apples": { "name": "maรงรฃs", - "plural_name": "apples" + "plural_name": "maรงรฃs" }, "artichoke": { "name": "alcachofra" @@ -48,7 +48,7 @@ }, "bell-peppers": { "name": "pimentรตes", - "plural_name": "bell peppers" + "plural_name": "pimentรตes" }, "blackberries": { "name": "amoras silvestres" @@ -94,7 +94,7 @@ }, "cabbage": { "name": "repolho", - "plural_name": "cabbages" + "plural_name": "repolhos" }, "cactus-edible": { "name": "cacto, comestรญvel" @@ -116,7 +116,7 @@ }, "carrot": { "name": "cenoura", - "plural_name": "carrots" + "plural_name": "cenouras" }, "caster-sugar": { "name": "aรงรบcar refinado" @@ -129,7 +129,7 @@ }, "cauliflower": { "name": "couve-flor", - "plural_name": "cauliflowers" + "plural_name": "couve-flor" }, "cayenne-pepper": { "name": "pimenta caiena" @@ -154,7 +154,7 @@ }, "chilli-peppers": { "name": "pimenta picante", - "plural_name": "chilli peppers" + "plural_name": "pimenta em pรณ" }, "chinese-leaves": { "name": "couve-china" @@ -176,7 +176,7 @@ }, "coconut": { "name": "coco", - "plural_name": "coconuts" + "plural_name": "cocos" }, "coconut-milk": { "name": "leite de coco" @@ -198,7 +198,7 @@ }, "corn": { "name": "milho", - "plural_name": "corns" + "plural_name": "milhos" }, "corn-syrup": { "name": "xarope de milho" @@ -214,14 +214,14 @@ }, "cucumber": { "name": "pepino", - "plural_name": "cucumbers" + "plural_name": "pepinos" }, "cumin": { "name": "cominho" }, "daikon": { "name": "rabanete", - "plural_name": "daikons" + "plural_name": "rabanetes" }, "dairy-products-and-dairy-substitutes": { "name": "produtos lรกcteos e substitutos de leite" @@ -240,15 +240,15 @@ }, "eggplant": { "name": "berinjela", - "plural_name": "eggplants" + "plural_name": "berinjelas" }, "eggs": { "name": "ovos", - "plural_name": "eggs" + "plural_name": "ovos" }, "endive": { "name": "endรญvia", - "plural_name": "endives" + "plural_name": "endรญvias" }, "fats": { "name": "gorduras" @@ -292,7 +292,7 @@ }, "garlic": { "name": "alho", - "plural_name": "garlics" + "plural_name": "alhos" }, "gem-squash": { "name": "abรณbora coroa" @@ -317,11 +317,11 @@ }, "green-onion": { "name": "Cebola Verde", - "plural_name": "green onions" + "plural_name": "cebolinha" }, "heart-of-palm": { "name": "palmito", - "plural_name": "heart of palms" + "plural_name": "palmito" }, "hemp": { "name": "cรขnhamo" @@ -337,7 +337,7 @@ }, "jackfruit": { "name": "jaca", - "plural_name": "jackfruits" + "plural_name": "jaca" }, "jaggery": { "name": "aรงรบcar mascavo" @@ -368,7 +368,7 @@ }, "leek": { "name": "alho-porrรณ", - "plural_name": "leeks" + "plural_name": "alhos-porrรณ" }, "legumes": { "name": "leguminosas" @@ -384,7 +384,7 @@ }, "liver": { "name": "fรญgado", - "plural_name": "livers" + "plural_name": "fรญgados" }, "maize": { "name": "milho" @@ -403,7 +403,7 @@ }, "mushroom": { "name": "cogumelo", - "plural_name": "mushrooms" + "plural_name": "cogumelos" }, "mussels": { "name": "mexilhรตes" @@ -425,7 +425,7 @@ }, "octopuses": { "name": "polvos", - "plural_name": "octopuses" + "plural_name": "polvos" }, "oils": { "name": "รณleos" @@ -450,7 +450,7 @@ }, "oranges": { "name": "laranjas", - "plural_name": "oranges" + "plural_name": "laranjas" }, "oregano": { "name": "orรฉgano" @@ -473,29 +473,29 @@ }, "pear": { "name": "pรชra", - "plural_name": "pears" + "plural_name": "peras" }, "peas": { "name": "ervilha" }, "pepper": { "name": "pimenta", - "plural_name": "peppers" + "plural_name": "pimentas" }, "pineapple": { "name": "abacaxi", - "plural_name": "pineapples" + "plural_name": "abacaxis" }, "plantain": { "name": "banana-da-terra", - "plural_name": "plantains" + "plural_name": "bananas-da-terra" }, "poppy-seeds": { "name": "sementes de papoula" }, "potato": { "name": "batata", - "plural_name": "potatoes" + "plural_name": "batatas" }, "poultry": { "name": "carne de frango" @@ -505,14 +505,14 @@ }, "pumpkin": { "name": "abรณbora", - "plural_name": "pumpkins" + "plural_name": "abรณboras" }, "pumpkin-seeds": { "name": "sementes de abรณbora" }, "radish": { "name": "rabanete", - "plural_name": "radishes" + "plural_name": "rabanetes" }, "raw-sugar": { "name": "aรงรบcar mascavo" @@ -543,7 +543,7 @@ }, "scallion": { "name": "cebolinha", - "plural_name": "scallions" + "plural_name": "cebolinhas" }, "seafood": { "name": "frutos do mar" @@ -556,7 +556,7 @@ }, "shallot": { "name": "chalota", - "plural_name": "shallots" + "plural_name": "chalota" }, "skate": { "name": "raia" diff --git a/mealie/repos/seed/resources/foods/locales/pt-PT.json b/mealie/repos/seed/resources/foods/locales/pt-PT.json index b80d4f66a..39031c5e8 100644 --- a/mealie/repos/seed/resources/foods/locales/pt-PT.json +++ b/mealie/repos/seed/resources/foods/locales/pt-PT.json @@ -26,7 +26,7 @@ "plural_name": "abacate" }, "bacon": { - "name": "bacon" + "name": "Carne fumada" }, "baking-powder": { "name": "fermento em pรณ" @@ -109,7 +109,7 @@ "name": "canรกbis" }, "capsicum": { - "name": "capsicum" + "name": "pimentรฃo" }, "caraway": { "name": "alcarรกvia" diff --git a/mealie/repos/seed/resources/foods/locales/sl-SI.json b/mealie/repos/seed/resources/foods/locales/sl-SI.json index d3a115747..deb515d63 100644 --- a/mealie/repos/seed/resources/foods/locales/sl-SI.json +++ b/mealie/repos/seed/resources/foods/locales/sl-SI.json @@ -361,7 +361,7 @@ "name": "kolerabe" }, "kumara": { - "name": "kumara" + "name": "sladek krompir" }, "leavening-agents": { "name": "kvas" @@ -377,7 +377,7 @@ "name": "limonina trava" }, "lentils": { - "name": "lentils" + "name": "leฤa" }, "lettuce": { "name": "solata" @@ -434,7 +434,7 @@ "name": "jedilni oslez" }, "olive": { - "name": "olive" + "name": "oliva" }, "olive-oil": { "name": "olivno olje" diff --git a/mealie/repos/seed/resources/foods/locales/sv-SE.json b/mealie/repos/seed/resources/foods/locales/sv-SE.json index bec124c3e..64f9e4fbe 100644 --- a/mealie/repos/seed/resources/foods/locales/sv-SE.json +++ b/mealie/repos/seed/resources/foods/locales/sv-SE.json @@ -616,7 +616,7 @@ }, "sweetcorn": { "name": "sockermajs", - "plural_name": "sweetcorns" + "plural_name": "majs" }, "sweeteners": { "name": "sรถtningsmedel" @@ -680,7 +680,7 @@ }, "yam": { "name": "jams", - "plural_name": "yams" + "plural_name": "sรถtpotatisar" }, "yeast": { "name": "jรคst" diff --git a/mealie/repos/seed/resources/labels/locales/sv-SE.json b/mealie/repos/seed/resources/labels/locales/sv-SE.json index 3707bf93a..26944d255 100644 --- a/mealie/repos/seed/resources/labels/locales/sv-SE.json +++ b/mealie/repos/seed/resources/labels/locales/sv-SE.json @@ -21,10 +21,10 @@ "name": "Drycker" }, { - "name": "Bakade varor" + "name": "Bakverk" }, { - "name": "Konserverade varor" + "name": "Konserver" }, { "name": "Smaktillsatser" diff --git a/mealie/repos/seed/resources/units/locales/fr-FR.json b/mealie/repos/seed/resources/units/locales/fr-FR.json index 512da5275..226901043 100644 --- a/mealie/repos/seed/resources/units/locales/fr-FR.json +++ b/mealie/repos/seed/resources/units/locales/fr-FR.json @@ -91,8 +91,8 @@ "abbreviation": "" }, "dash": { - "name": "1/8 de cuillรจre ร  cafรฉ", - "plural_name": "1/8 de cuillรจres ร  cafรฉ", + "name": "goutte", + "plural_name": "gouttes", "description": "", "abbreviation": "" }, @@ -103,8 +103,8 @@ "abbreviation": "" }, "head": { - "name": "tรชte", - "plural_name": "tรชtes", + "name": "personne", + "plural_name": "personnes", "description": "", "abbreviation": "" }, diff --git a/mealie/repos/seed/resources/units/locales/gl-ES.json b/mealie/repos/seed/resources/units/locales/gl-ES.json index fc98d1d27..2c96a3abd 100644 --- a/mealie/repos/seed/resources/units/locales/gl-ES.json +++ b/mealie/repos/seed/resources/units/locales/gl-ES.json @@ -3,11 +3,11 @@ "name": "teaspoon", "plural_name": "teaspoons", "description": "", - "abbreviation": "culleradiรฑa" + "abbreviation": "culleriรฑa" }, "tablespoon": { - "name": "tablespoon", - "plural_name": "tablespoons", + "name": "culler de sopa", + "plural_name": "culleres de sopa", "description": "", "abbreviation": "culleradas sopeiras" }, @@ -36,33 +36,33 @@ "abbreviation": "qt" }, "gallon": { - "name": "gallon", - "plural_name": "gallons", + "name": "galรณn", + "plural_name": "galรณns", "description": "", "abbreviation": "gal" }, "milliliter": { - "name": "milliliter", - "plural_name": "milliliters", + "name": "mililitro", + "plural_name": "mililitros", "description": "", "abbreviation": "ml" }, "liter": { - "name": "liter", - "plural_name": "liters", + "name": "litro", + "plural_name": "litros", "description": "", "abbreviation": "l" }, "pound": { - "name": "pound", - "plural_name": "pounds", + "name": "libra", + "plural_name": "libras", "description": "", "abbreviation": "lb", "plural_abbreviation": "lbs" }, "ounce": { - "name": "ounce", - "plural_name": "ounces", + "name": "onza", + "plural_name": "onzas", "description": "", "abbreviation": "oz" }, @@ -109,14 +109,14 @@ "abbreviation": "" }, "clove": { - "name": "clove", - "plural_name": "cloves", + "name": "dente", + "plural_name": "dentes", "description": "", "abbreviation": "" }, "can": { - "name": "can", - "plural_name": "cans", + "name": "lata", + "plural_name": "latas", "description": "", "abbreviation": "" }, diff --git a/mealie/repos/seed/resources/units/locales/pt-BR.json b/mealie/repos/seed/resources/units/locales/pt-BR.json index 63d7d246a..162f375ad 100644 --- a/mealie/repos/seed/resources/units/locales/pt-BR.json +++ b/mealie/repos/seed/resources/units/locales/pt-BR.json @@ -104,13 +104,13 @@ }, "head": { "name": "cabeรงa", - "plural_name": "heads", + "plural_name": "cabeรงas", "description": "", "abbreviation": "" }, "clove": { "name": "cravo", - "plural_name": "cloves", + "plural_name": "cravos", "description": "", "abbreviation": "" }, @@ -127,8 +127,8 @@ "abbreviation": "" }, "pack": { - "name": "pack", - "plural_name": "packs", + "name": "pacote", + "plural_name": "pacotes", "description": "", "abbreviation": "" }, diff --git a/mealie/repos/seed/resources/units/locales/sv-SE.json b/mealie/repos/seed/resources/units/locales/sv-SE.json index 7b8260ae6..5f3b35328 100644 --- a/mealie/repos/seed/resources/units/locales/sv-SE.json +++ b/mealie/repos/seed/resources/units/locales/sv-SE.json @@ -31,7 +31,7 @@ }, "quart": { "name": "quart", - "plural_name": "quarts", + "plural_name": "quart", "description": "", "abbreviation": "qt" }, diff --git a/mealie/repos/seed/resources/units/locales/uk-UA.json b/mealie/repos/seed/resources/units/locales/uk-UA.json index 54d670949..c2b5a1b2a 100644 --- a/mealie/repos/seed/resources/units/locales/uk-UA.json +++ b/mealie/repos/seed/resources/units/locales/uk-UA.json @@ -86,13 +86,13 @@ }, "splash": { "name": "ั‚ั€ะพั…ะธ", - "plural_name": "splashes", + "plural_name": "ั‚ั€ะพั…ะธ", "description": "", "abbreviation": "" }, "dash": { "name": "ั‚ั€ะพั…ะธ", - "plural_name": "dashes", + "plural_name": "ั‚ั€ะพั…ะธ", "description": "", "abbreviation": "" }, diff --git a/mealie/routes/recipe/shared_routes.py b/mealie/routes/recipe/shared_routes.py index eb8eb99d4..b92bd5a33 100644 --- a/mealie/routes/recipe/shared_routes.py +++ b/mealie/routes/recipe/shared_routes.py @@ -2,6 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import UUID4 from sqlalchemy.orm.session import Session +from mealie.core.root_logger import get_logger from mealie.db.db_setup import generate_session from mealie.repos.all_repositories import get_repositories from mealie.schema.recipe import Recipe @@ -9,12 +10,22 @@ from mealie.schema.response import ErrorResponse router = APIRouter() +logger = get_logger() + @router.get("/shared/{token_id}", response_model=Recipe) def get_shared_recipe(token_id: UUID4, session: Session = Depends(generate_session)): db = get_repositories(session, group_id=None, household_id=None) token_summary = db.recipe_share_tokens.get_one(token_id) + if token_summary and token_summary.is_expired: + try: + db.recipe_share_tokens.delete(token_id) + session.commit() + except Exception: + logger.exception(f"Failed to delete expired token {token_id}") + session.rollback() + token_summary = None if token_summary is None: raise HTTPException(status_code=404, detail=ErrorResponse.respond("Token Not Found")) diff --git a/mealie/routes/shared/__init__.py b/mealie/routes/shared/__init__.py index cad900646..61bb31efd 100644 --- a/mealie/routes/shared/__init__.py +++ b/mealie/routes/shared/__init__.py @@ -3,6 +3,7 @@ from functools import cached_property from fastapi import HTTPException from pydantic import UUID4 +from mealie.repos.all_repositories import get_repositories from mealie.routes._base import BaseUserController, controller from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.routers import UserAPIRouter @@ -32,7 +33,8 @@ class RecipeSharedController(BaseUserController): @router.post("", response_model=RecipeShareToken, status_code=201) def create_one(self, data: RecipeShareTokenCreate) -> RecipeShareToken: # check if recipe group id is the same as the user group id - recipe = self.repos.recipes.get_one(data.recipe_id, "id") + group_repos = get_repositories(self.repos.session, group_id=self.group_id, household_id=None) + recipe = group_repos.recipes.get_one(data.recipe_id, "id") if recipe is None or recipe.group_id != self.group_id: raise HTTPException(status_code=404, detail="Recipe not found in your group") diff --git a/mealie/schema/recipe/recipe_share_token.py b/mealie/schema/recipe/recipe_share_token.py index 0e876a05a..918f2dc7e 100644 --- a/mealie/schema/recipe/recipe_share_token.py +++ b/mealie/schema/recipe/recipe_share_token.py @@ -18,6 +18,10 @@ class RecipeShareTokenCreate(MealieModel): recipe_id: UUID4 expires_at: datetime = Field(default_factory=defaut_expires_at_time) + @property + def is_expired(self) -> bool: + return self.expires_at < datetime.now(UTC) + class RecipeShareTokenSave(RecipeShareTokenCreate): group_id: UUID4 diff --git a/mealie/services/openai/openai.py b/mealie/services/openai/openai.py index 09c391d56..5d74d9309 100644 --- a/mealie/services/openai/openai.py +++ b/mealie/services/openai/openai.py @@ -7,7 +7,7 @@ from pathlib import Path from textwrap import dedent from openai import NOT_GIVEN, AsyncOpenAI -from openai.resources.chat.completions import ChatCompletion +from openai.types.chat import ChatCompletion from pydantic import BaseModel, field_validator from mealie.core.config import get_app_settings diff --git a/mealie/services/recipe/recipe_data_service.py b/mealie/services/recipe/recipe_data_service.py index a4d0432ac..f92270aff 100644 --- a/mealie/services/recipe/recipe_data_service.py +++ b/mealie/services/recipe/recipe_data_service.py @@ -9,13 +9,7 @@ from mealie.pkgs import img, safehttp from mealie.pkgs.safehttp.transport import AsyncSafeTransport from mealie.schema.recipe.recipe import Recipe from mealie.services._base_service import BaseService - -try: - from recipe_scrapers._abstract import HEADERS - - _FIREFOX_UA = HEADERS["User-Agent"] -except (ImportError, KeyError): - _FIREFOX_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/128.0" +from mealie.services.scraper.user_agents_manager import get_user_agents_manager async def gather_with_concurrency(n, *coros, ignore_exceptions=False): @@ -32,13 +26,15 @@ async def gather_with_concurrency(n, *coros, ignore_exceptions=False): async def largest_content_len(urls: list[str]) -> tuple[str, int]: + user_agent_manager = get_user_agents_manager() + largest_url = "" largest_len = 0 max_concurrency = 10 async def do(client: AsyncClient, url: str) -> Response: - return await client.head(url, headers={"User-Agent": _FIREFOX_UA}) + return await client.head(url, headers=user_agent_manager.get_scrape_headers()) async with AsyncClient(transport=safehttp.AsyncSafeTransport()) as client: tasks = [do(client, url) for url in urls] @@ -110,6 +106,7 @@ class RecipeDataService(BaseService): async def scrape_image(self, image_url: str | dict[str, str] | list[str]) -> None: self.logger.info(f"Image URL: {image_url}") + user_agent = get_user_agents_manager().user_agents[0] image_url_str = "" @@ -140,7 +137,7 @@ class RecipeDataService(BaseService): async with AsyncClient(transport=AsyncSafeTransport()) as client: try: - r = await client.get(image_url_str, headers={"User-Agent": _FIREFOX_UA}) + r = await client.get(image_url_str, headers={"User-Agent": user_agent}) except Exception: self.logger.exception("Fatal Image Request Exception") return None diff --git a/mealie/services/scheduler/tasks/__init__.py b/mealie/services/scheduler/tasks/__init__.py index 2c7c850f7..b918257e7 100644 --- a/mealie/services/scheduler/tasks/__init__.py +++ b/mealie/services/scheduler/tasks/__init__.py @@ -1,6 +1,7 @@ from .create_timeline_events import create_mealplan_timeline_events from .delete_old_checked_shopping_list_items import delete_old_checked_list_items from .post_webhooks import post_group_webhooks +from .purge_expired_share_tokens import purge_expired_tokens from .purge_group_exports import purge_group_data_exports from .purge_password_reset import purge_password_reset_tokens from .purge_registration import purge_group_registration @@ -10,6 +11,7 @@ __all__ = [ "create_mealplan_timeline_events", "delete_old_checked_list_items", "post_group_webhooks", + "purge_expired_tokens", "purge_password_reset_tokens", "purge_group_data_exports", "purge_group_registration", diff --git a/mealie/services/scheduler/tasks/purge_expired_share_tokens.py b/mealie/services/scheduler/tasks/purge_expired_share_tokens.py new file mode 100644 index 000000000..00da5ceb8 --- /dev/null +++ b/mealie/services/scheduler/tasks/purge_expired_share_tokens.py @@ -0,0 +1,19 @@ +from datetime import UTC, datetime + +from mealie.db.db_setup import session_context +from mealie.repos.all_repositories import get_repositories +from mealie.schema.response.pagination import PaginationQuery + + +def purge_expired_tokens() -> None: + current_time = datetime.now(UTC) + + with session_context() as session: + db = get_repositories(session, group_id=None) + tokens_response = db.recipe_share_tokens.page_all( + PaginationQuery(page=1, per_page=-1, query_filter=f"expiresAt < {current_time}") + ) + if not (tokens := tokens_response.items): + return + + db.recipe_share_tokens.delete_many([token.id for token in tokens]) diff --git a/mealie/services/scraper/scraper_strategies.py b/mealie/services/scraper/scraper_strategies.py index 9cd01b76e..35fdfca72 100644 --- a/mealie/services/scraper/scraper_strategies.py +++ b/mealie/services/scraper/scraper_strategies.py @@ -6,7 +6,7 @@ from typing import Any import bs4 import extruct from fastapi import HTTPException, status -from httpx import AsyncClient +from httpx import AsyncClient, Response from recipe_scrapers import NoSchemaFoundInWildMode, SchemaScraperFactory, scrape_html from slugify import slugify from w3lib.html import get_base_url @@ -20,16 +20,10 @@ from mealie.services.openai import OpenAIService from mealie.services.scraper.scraped_extras import ScrapedExtras from . import cleaner - -try: - from recipe_scrapers._abstract import HEADERS - - _FIREFOX_UA = HEADERS["User-Agent"] -except (ImportError, KeyError): - _FIREFOX_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/128.0" - +from .user_agents_manager import get_user_agents_manager SCRAPER_TIMEOUT = 15 +logger = get_logger() class ForceTimeoutException(Exception): @@ -42,32 +36,50 @@ async def safe_scrape_html(url: str) -> str: if the request takes longer than 15 seconds. This is used to mitigate DDOS attacks from users providing a url with arbitrary large content. """ + user_agents_manager = get_user_agents_manager() + + logger.debug(f"Scraping URL: {url}") async with AsyncClient(transport=safehttp.AsyncSafeTransport()) as client: - html_bytes = b"" - async with client.stream( - "GET", url, timeout=SCRAPER_TIMEOUT, headers={"User-Agent": _FIREFOX_UA}, follow_redirects=True - ) as resp: - start_time = time.time() + for user_agent in user_agents_manager.user_agents: + logger.debug(f'Trying User-Agent: "{user_agent}"') - async for chunk in resp.aiter_bytes(chunk_size=1024): - html_bytes += chunk + response: Response | None = None + html_bytes = b"" + async with client.stream( + "GET", + url, + timeout=SCRAPER_TIMEOUT, + headers=user_agents_manager.get_scrape_headers(user_agent), + follow_redirects=True, + ) as resp: + if resp.status_code == status.HTTP_403_FORBIDDEN: + logger.debug(f'403 Forbidden with User-Agent: "{user_agent}"') + continue - if time.time() - start_time > SCRAPER_TIMEOUT: - raise ForceTimeoutException() + start_time = time.time() + + async for chunk in resp.aiter_bytes(chunk_size=1024): + html_bytes += chunk + + if time.time() - start_time > SCRAPER_TIMEOUT: + raise ForceTimeoutException() + + response = resp + break + + if not (response and html_bytes): + return "" # ===================================== # Copied from requests text property # Try charset from content-type content = None - encoding = resp.encoding - - if not html_bytes: - return "" + encoding = response.encoding # Fallback to auto-detected encoding. if encoding is None: - encoding = resp.apparent_encoding + encoding = response.apparent_encoding # Decode unicode from given encoding. try: diff --git a/mealie/services/scraper/user-agents.txt b/mealie/services/scraper/user-agents.txt new file mode 100644 index 000000000..9b67731d8 --- /dev/null +++ b/mealie/services/scraper/user-agents.txt @@ -0,0 +1,3 @@ +Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604. +Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0. +Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.164 Mobile Safari/537.36 EdgA/131.0.2903.87 diff --git a/mealie/services/scraper/user_agents_manager.py b/mealie/services/scraper/user_agents_manager.py new file mode 100644 index 000000000..531380480 --- /dev/null +++ b/mealie/services/scraper/user_agents_manager.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import os +import random + +_USER_AGENTS_MANAGER: UserAgentsManager | None = None + + +def get_user_agents_manager() -> UserAgentsManager: + global _USER_AGENTS_MANAGER + + if not _USER_AGENTS_MANAGER: + _USER_AGENTS_MANAGER = UserAgentsManager() + + return _USER_AGENTS_MANAGER + + +class UserAgentsManager: + def __init__(self) -> None: + self._user_agents: list[str] | None = None + self._user_agents_text_path = os.path.join(os.path.dirname(__file__), "user-agents.txt") + + def get_scrape_headers(self, user_agent: str | None = None) -> dict[str, str]: + # From: https://scrapeops.io/web-scraping-playbook/403-forbidden-error-web-scraping/#optimize-request-headers + if user_agent is None: + user_agent = random.choice(self.user_agents) + + return { + "User-Agent": user_agent, + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + "Cache-Control": "max-age=0", + } + + @property + def user_agents(self) -> list[str]: + if not self._user_agents: + self._user_agents = self._fetch_user_agents() + + return self._user_agents + + def _fetch_user_agents(self) -> list[str]: + user_agents: list[str] = [] + + try: + from recipe_scrapers._abstract import HEADERS + + user_agents.append(HEADERS["User-Agent"]) + except (ImportError, KeyError): + user_agents.append("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/128.0") + + with open(self._user_agents_text_path) as f: + for line in f: + if not line: + continue + user_agents.append(line.strip()) + + return user_agents diff --git a/poetry.lock b/poetry.lock index 99a4f7f25..88a10d778 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. [[package]] name = "aiofiles" @@ -6,6 +6,7 @@ version = "24.1.0" description = "File support for asyncio." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -17,6 +18,7 @@ version = "1.14.1" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"}, {file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"}, @@ -36,6 +38,7 @@ version = "10.0.0" description = "A library for parsing ISO 8601 strings." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "aniso8601-10.0.0-py2.py3-none-any.whl", hash = "sha256:3c943422efaa0229ebd2b0d7d223effb5e7c89e24d2267ebe76c61a2d8e290cb"}, {file = "aniso8601-10.0.0.tar.gz", hash = "sha256:ff1d0fc2346688c62c0151547136ac30e322896ed8af316ef7602c47da9426cf"}, @@ -50,6 +53,7 @@ version = "0.6.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, @@ -61,6 +65,7 @@ version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, @@ -81,6 +86,7 @@ version = "1.4.4" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, @@ -92,6 +98,7 @@ version = "1.9.2" description = "Push Notifications that work with just about every platform!" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "apprise-1.9.2-py3-none-any.whl", hash = "sha256:52bd580058d8534b204bfc9ca53b5810c6a935d1bed7c463147e244b2c93362b"}, {file = "apprise-1.9.2.tar.gz", hash = "sha256:290eb1217028dd5040802371494d70c1bcab907e6634a77c263489dbe4fd73a8"}, @@ -111,6 +118,7 @@ version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, @@ -118,13 +126,14 @@ files = [ [[package]] name = "authlib" -version = "1.4.1" +version = "1.5.0" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "Authlib-1.4.1-py2.py3-none-any.whl", hash = "sha256:edc29c3f6a3e72cd9e9f45fff67fc663a2c364022eb0371c003f22d5405915c1"}, - {file = "authlib-1.4.1.tar.gz", hash = "sha256:30ead9ea4993cdbab821dc6e01e818362f92da290c04c7f6a1940f86507a790d"}, + {file = "Authlib-1.5.0-py2.py3-none-any.whl", hash = "sha256:b3cc5ccfc19cf87678046b6e7cb19d402d8a631a33c40e36385232203227953a"}, + {file = "authlib-1.5.0.tar.gz", hash = "sha256:8fd8bd8f806485a532ac39a17b579982cf54688f956174f995cc938a91725423"}, ] [package.dependencies] @@ -136,6 +145,7 @@ version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, @@ -150,6 +160,7 @@ version = "4.2.1" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"}, {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"}, @@ -184,17 +195,19 @@ typecheck = ["mypy"] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.3" description = "Screen-scraping library" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.7.0" +groups = ["main"] files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, + {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, ] [package.dependencies] soupsieve = ">1.2" +typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] @@ -209,6 +222,7 @@ version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, @@ -220,6 +234,7 @@ version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, @@ -284,6 +299,7 @@ version = "3.3.1" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.6.1" +groups = ["dev"] files = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -295,6 +311,7 @@ version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, @@ -392,6 +409,7 @@ version = "8.1.3" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, @@ -406,80 +424,84 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "coverage" -version = "7.6.10" +version = "7.6.12" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, - {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, - {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, - {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, - {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, - {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, - {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, - {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, - {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, - {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, - {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, - {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, - {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, - {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, - {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, - {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, + {file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"}, + {file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"}, + {file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"}, + {file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"}, + {file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"}, + {file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"}, + {file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"}, + {file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"}, + {file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"}, + {file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"}, + {file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"}, + {file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"}, + {file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"}, + {file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"}, + {file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"}, + {file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"}, + {file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"}, + {file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"}, + {file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"}, + {file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"}, + {file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"}, + {file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"}, + {file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"}, + {file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"}, + {file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"}, + {file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"}, + {file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"}, ] [package.extras] @@ -491,6 +513,7 @@ version = "0.1.2" description = "A simple .coverage to LCOV converter" optional = false python-versions = ">=3.6.3,<4.0.0" +groups = ["dev"] files = [ {file = "coveragepy-lcov-0.1.2.tar.gz", hash = "sha256:db6ad0d255d3a8041d30e797a9ec69c77c5963694a6b02a00907b56db7b882a3"}, {file = "coveragepy_lcov-0.1.2-py3-none-any.whl", hash = "sha256:7c1e454ada324a1f47fd7cd2de2c6349b9822cc79691b313ed10370e7ce1b08b"}, @@ -506,6 +529,7 @@ version = "41.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, @@ -551,6 +575,7 @@ version = "0.3.7" description = "serialize all of Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, @@ -565,6 +590,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -576,6 +602,7 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -587,6 +614,7 @@ version = "0.18.0" description = "Extract embedded metadata from HTML markup" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "extruct-0.18.0-py2.py3-none-any.whl", hash = "sha256:1e739985da705c3348c7614dc169e7780caf20908338fa5f4c6e48576df6f000"}, {file = "extruct-0.18.0.tar.gz", hash = "sha256:b5b48d459003b27c05ee91527b14a5a31735231aaf85d2b1f331d4db879318dd"}, @@ -611,6 +639,7 @@ version = "0.115.8" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"}, {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"}, @@ -631,6 +660,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -647,6 +677,7 @@ version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, @@ -661,6 +692,7 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -678,6 +710,8 @@ version = "3.0.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, @@ -749,6 +783,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -760,6 +795,7 @@ version = "0.5.2" description = "Extract text from HTML" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "html_text-0.5.2-py2.py3-none-any.whl", hash = "sha256:3f1e063f05eddf3e099a88f0440219c55fdc01c44f1291fe59c66e5228d7fc56"}, {file = "html_text-0.5.2.tar.gz", hash = "sha256:afd61bbb70651d494a8c32670a29b9140492eccc9690109857beae41c3093ded"}, @@ -774,6 +810,7 @@ version = "2024.2.26" description = "Turn HTML into equivalent Markdown-structured text." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32"}, ] @@ -784,6 +821,7 @@ version = "1.1" description = "HTML parser based on the WHATWG HTML specification" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, @@ -805,6 +843,7 @@ version = "1.0.2" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, @@ -826,6 +865,7 @@ version = "0.6.4" description = "A collection of framework independent HTTP protocol utils." optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, @@ -881,6 +921,7 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -905,6 +946,7 @@ version = "2.5.17" description = "File identification library for Python" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "identify-2.5.17-py2.py3-none-any.whl", hash = "sha256:7d526dd1283555aafcc91539acc061d8f6f59adb0a7bba462735b0a318bff7ed"}, {file = "identify-2.5.17.tar.gz", hash = "sha256:93cc61a861052de9d4c541a7acb7e3dcc9c11b398a2144f6e52ae5285f5f4f06"}, @@ -919,6 +961,7 @@ version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" +groups = ["main", "dev"] files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, @@ -930,6 +973,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -941,6 +985,7 @@ version = "0.7.2" description = "An ISO 8601 date/time/duration parser and formatter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15"}, {file = "isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6"}, @@ -952,6 +997,7 @@ version = "5.12.0" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, @@ -969,6 +1015,7 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -980,6 +1027,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -997,6 +1045,7 @@ version = "0.5.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, @@ -1067,163 +1116,165 @@ version = "0.0.2" description = "Library to parse JSON with js-style comments." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "jstyleson-0.0.2.tar.gz", hash = "sha256:680003f3b15a2959e4e6a351f3b858e3c07dd3e073a0d54954e34d8ea5e1308e"}, ] [[package]] name = "lxml" -version = "5.3.0" +version = "5.3.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ - {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, - {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"}, - {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"}, - {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"}, - {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"}, - {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"}, - {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"}, - {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"}, - {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"}, - {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"}, - {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"}, - {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"}, - {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"}, - {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"}, - {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"}, - {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"}, - {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"}, - {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"}, - {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"}, - {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"}, - {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"}, - {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"}, - {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"}, - {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"}, - {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"}, - {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"}, - {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"}, - {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"}, - {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"}, - {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"}, - {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"}, - {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"}, - {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"}, - {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"}, - {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"}, - {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"}, - {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"}, - {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"}, - {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"}, - {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"}, - {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"}, - {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"}, - {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"}, - {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"}, - {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"}, - {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"}, - {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"}, - {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"}, - {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"}, - {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"}, - {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"}, - {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"}, + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b"}, + {file = "lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79"}, + {file = "lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5"}, + {file = "lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0"}, + {file = "lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23"}, + {file = "lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f"}, + {file = "lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629"}, + {file = "lxml-5.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c"}, + {file = "lxml-5.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff"}, + {file = "lxml-5.3.1-cp311-cp311-win32.whl", hash = "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2"}, + {file = "lxml-5.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c"}, + {file = "lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468"}, + {file = "lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f"}, + {file = "lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645"}, + {file = "lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5"}, + {file = "lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e"}, + {file = "lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8"}, + {file = "lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5"}, + {file = "lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252"}, + {file = "lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78"}, + {file = "lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332"}, + {file = "lxml-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac"}, + {file = "lxml-5.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d"}, + {file = "lxml-5.3.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a"}, + {file = "lxml-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576"}, + {file = "lxml-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9"}, + {file = "lxml-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70"}, + {file = "lxml-5.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53"}, + {file = "lxml-5.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce"}, + {file = "lxml-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407"}, + {file = "lxml-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5"}, + {file = "lxml-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf"}, + {file = "lxml-5.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb"}, + {file = "lxml-5.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73"}, + {file = "lxml-5.3.1-cp38-cp38-win32.whl", hash = "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc"}, + {file = "lxml-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f"}, + {file = "lxml-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b"}, + {file = "lxml-5.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2"}, + {file = "lxml-5.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af"}, + {file = "lxml-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0"}, + {file = "lxml-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877"}, + {file = "lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98"}, + {file = "lxml-5.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac"}, + {file = "lxml-5.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe"}, + {file = "lxml-5.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2"}, + {file = "lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] -html-clean = ["lxml-html-clean"] +html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.11)"] +source = ["Cython (>=3.0.11,<3.1.0)"] [[package]] name = "lxml-html-clean" @@ -1231,6 +1282,7 @@ version = "0.1.1" description = "HTML cleaner from lxml project" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "lxml_html_clean-0.1.1-py3-none-any.whl", hash = "sha256:58c04176593c9caf72ec92e033d2f38859e918b3eff0cc0f8051ad27dc2ab8ef"}, {file = "lxml_html_clean-0.1.1.tar.gz", hash = "sha256:8a644ed01dbbe132fabddb9467f077f6dad12a1d4f3a6a553e280f3815fa46df"}, @@ -1245,6 +1297,7 @@ version = "1.2.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, @@ -1264,6 +1317,7 @@ version = "3.3.7" description = "Python implementation of Markdown." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, @@ -1278,6 +1332,7 @@ version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, @@ -1302,6 +1357,7 @@ version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, @@ -1361,6 +1417,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1372,6 +1429,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1383,6 +1441,7 @@ version = "1.3.4" description = "A deep merge function for ๐Ÿ." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -1390,18 +1449,20 @@ files = [ [[package]] name = "mf2py" -version = "1.1.2" -description = "Python Microformats2 parser" +version = "2.0.1" +description = "Microformats parser" optional = false -python-versions = "*" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "mf2py-1.1.2.tar.gz", hash = "sha256:84f1f8f2ff3f1deb1c30be497e7ccd805452996a662fd4a77f09e0105bede2c9"}, + {file = "mf2py-2.0.1-py3-none-any.whl", hash = "sha256:092806e17f1a93db4aafa5e8d3c4124b5e42cd89027e2db48a5248ef4eabde03"}, + {file = "mf2py-2.0.1.tar.gz", hash = "sha256:1380924633413b8d72e704b5c86b4382c4b1371699edecc907b01cd21138d7cd"}, ] [package.dependencies] -BeautifulSoup4 = ">=4.6.0" -html5lib = ">=1.0.1" -requests = ">=2.18.4" +beautifulsoup4 = ">=4.11.1,<5.0.0" +html5lib = ">=1.1,<2.0" +requests = ">=2.28.2,<3.0.0" [[package]] name = "mkdocs" @@ -1409,6 +1470,7 @@ version = "1.6.0" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, @@ -1439,6 +1501,7 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -1451,13 +1514,14 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.6.2" +version = "9.6.5" description = "Documentation that simply works" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "mkdocs_material-9.6.2-py3-none-any.whl", hash = "sha256:71d90dbd63b393ad11a4d90151dfe3dcbfcd802c0f29ce80bebd9bbac6abc753"}, - {file = "mkdocs_material-9.6.2.tar.gz", hash = "sha256:a3de1c5d4c745f10afa78b1a02f917b9dce0808fb206adc0f5bb48b58c1ca21f"}, + {file = "mkdocs_material-9.6.5-py3-none-any.whl", hash = "sha256:aad3e6fb860c20870f75fb2a69ef901f1be727891e41adb60b753efcae19453b"}, + {file = "mkdocs_material-9.6.5.tar.gz", hash = "sha256:b714679a8c91b0ffe2188e11ed58c44d2523e9c2ae26a29cc652fa7478faa21f"}, ] [package.dependencies] @@ -1484,6 +1548,7 @@ version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -1495,6 +1560,7 @@ version = "1.15.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, @@ -1547,6 +1613,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1558,6 +1625,7 @@ version = "1.7.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +groups = ["dev"] files = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, @@ -1572,6 +1640,7 @@ version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, @@ -1584,13 +1653,14 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.61.0" +version = "1.64.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "openai-1.61.0-py3-none-any.whl", hash = "sha256:e8c512c0743accbdbe77f3429a1490d862f8352045de8dc81969301eb4a4f666"}, - {file = "openai-1.61.0.tar.gz", hash = "sha256:216f325a24ed8578e929b0f1b3fb2052165f3b04b0461818adaa51aa29c71f8a"}, + {file = "openai-1.64.0-py3-none-any.whl", hash = "sha256:20f85cde9e95e9fbb416e3cb5a6d3119c0b28308afd6e3cc47bf100623dac623"}, + {file = "openai-1.64.0.tar.gz", hash = "sha256:2861053538704d61340da56e2f176853d19f1dc5704bc306b7597155f850d57a"}, ] [package.dependencies] @@ -1613,6 +1683,7 @@ version = "3.10.15" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"}, {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"}, @@ -1701,6 +1772,7 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -1712,6 +1784,7 @@ version = "0.5.6" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, ] @@ -1722,6 +1795,7 @@ version = "1.6.1" description = "MQTT version 5.0/3.1.1 client class" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"}, ] @@ -1735,6 +1809,7 @@ version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, @@ -1746,6 +1821,7 @@ version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -1834,6 +1910,7 @@ version = "0.21.0" description = "Python interface for libheif library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pillow_heif-0.21.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:f54609401164b0cb58000bd2516a88516b5e3e9b2f9c52ad9500575f1851da5e"}, {file = "pillow_heif-0.21.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:d0a68246340d4fad4f10721a1a50b87a7011f1bd18d0a7b7d231e196776d0260"}, @@ -1898,6 +1975,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1914,6 +1992,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1929,6 +2008,7 @@ version = "4.1.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, @@ -1947,6 +2027,7 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.8" +groups = ["main", "postgres"] files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -2024,6 +2105,7 @@ version = "0.4.8" description = "ASN.1 types and codecs" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, @@ -2035,6 +2117,7 @@ version = "0.2.8" description = "A collection of ASN.1-based protocols modules." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, @@ -2049,6 +2132,7 @@ version = "2.21" description = "C parser in Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, @@ -2056,13 +2140,14 @@ files = [ [[package]] name = "pydantic" -version = "2.10.5" +version = "2.10.6" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ - {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, - {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, ] [package.dependencies] @@ -2080,6 +2165,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -2188,13 +2274,14 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.7.1" +version = "2.8.0" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd"}, - {file = "pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93"}, + {file = "pydantic_settings-2.8.0-py3-none-any.whl", hash = "sha256:c782c7dc3fb40e97b238e713c25d26f64314aece2e91abcff592fcac15f71820"}, + {file = "pydantic_settings-2.8.0.tar.gz", hash = "sha256:88e2ca28f6e68ea102c99c3c401d6c9078e68a5df600e97b43891c34e089500a"}, ] [package.dependencies] @@ -2212,6 +2299,7 @@ version = "1.0.6" description = "Convert pydantic v1 and pydantic v2 models to typescript interfaces" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pydantic-to-typescript2-1.0.6.tar.gz", hash = "sha256:19cc0fb03802abcb508b02fbc334f1667ff50e0853a782b58df9dd0409290163"}, {file = "pydantic_to_typescript2-1.0.6-py3-none-any.whl", hash = "sha256:89bbdd4b84b72d9f8ada33fd4d7d6605457be302dd6d4c6d48faa9310841bb69"}, @@ -2229,6 +2317,7 @@ version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, @@ -2244,6 +2333,7 @@ version = "3.8.0" description = "๐Ÿซ Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, @@ -2255,6 +2345,7 @@ version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -2272,6 +2363,7 @@ version = "3.3.4" description = "python code static checker" optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "pylint-3.3.4-py3-none-any.whl", hash = "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018"}, {file = "pylint-3.3.4.tar.gz", hash = "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce"}, @@ -2296,6 +2388,7 @@ version = "10.4" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pymdown_extensions-10.4-py3-none-any.whl", hash = "sha256:cfc28d6a09d19448bcbf8eee3ce098c7d17ff99f7bd3069db4819af181212037"}, {file = "pymdown_extensions-10.4.tar.gz", hash = "sha256:bc46f11749ecd4d6b71cf62396104b4a200bad3498cb0f5dad1b8502fe461a35"}, @@ -2314,6 +2407,7 @@ version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" +groups = ["main"] files = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, @@ -2324,19 +2418,20 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyrdfa3" -version = "3.6.2" +version = "3.6.4" description = "pyRdfa distiller/parser library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "pyRdfa3-3.6.2-py3-none-any.whl", hash = "sha256:290c2fa966ddd1b45ac94a727da144f5a233ed58c63c370e3d68e6d00b0dee5d"}, - {file = "pyRdfa3-3.6.2.tar.gz", hash = "sha256:73681dab957f60901696767388b956a5769c730bc451da6ffb2f0e36f18314c2"}, + {file = "pyRdfa3-3.6.4-py3-none-any.whl", hash = "sha256:ed11affa5567ab7afdbc939a58f9286a274447f3ab2999c260c56b5c6e87fb2f"}, + {file = "pyrdfa3-3.6.4.tar.gz", hash = "sha256:64712d1a4bf21829652b39715bada6e7c03bcf19cb49f962c190a38f46172243"}, ] [package.dependencies] html5lib = ">=1.1" -rdflib = ">=6.1.1" -requests = ">=2.25.1" +rdflib = ">=7.0.0" +requests = ">=2.32.3" [[package]] name = "pytest" @@ -2344,6 +2439,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -2364,6 +2460,7 @@ version = "0.25.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, @@ -2382,6 +2479,7 @@ version = "2.9.0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.tar.gz", hash = "sha256:78e73e19c63f5b20ffa567001531680d939dc042bf7850431877645523c66709"}, {file = "python_dateutil-2.9.0-py2.py3-none-any.whl", hash = "sha256:cbf2f1da5e6083ac2fbfd4da39a25f34312230110440f424a14c7558bb85d82e"}, @@ -2396,6 +2494,7 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -2410,6 +2509,7 @@ version = "3.4.4" description = "Python modules for implementing LDAP clients" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "python-ldap-3.4.4.tar.gz", hash = "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828"}, ] @@ -2424,6 +2524,7 @@ version = "0.0.20" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, @@ -2435,6 +2536,7 @@ version = "8.0.4" description = "A Python slugify application that also handles Unicode" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, @@ -2452,6 +2554,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2514,6 +2617,7 @@ version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -2528,6 +2632,7 @@ version = "3.12.1" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbb7ea2fd786e6d66f225ef6eef1728832314f47e82fee877cb2a793ebda9579"}, {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ae41361de05762c1eaa3955e5355de7c4c6f30d1ef1ea23d29bf738a35809ab"}, @@ -2624,37 +2729,36 @@ all = ["numpy"] [[package]] name = "rdflib" -version = "6.2.0" +version = "7.1.3" description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." optional = false -python-versions = ">=3.7" +python-versions = "<4.0.0,>=3.8.1" +groups = ["main"] files = [ - {file = "rdflib-6.2.0-py3-none-any.whl", hash = "sha256:85c34a86dfc517a41e5f2425a41a0aceacc23983462b32e68610b9fad1383bca"}, - {file = "rdflib-6.2.0.tar.gz", hash = "sha256:62dc3c86d1712db0f55785baf8047f63731fa59b2682be03219cb89262065942"}, + {file = "rdflib-7.1.3-py3-none-any.whl", hash = "sha256:5402310a9f0f3c07d453d73fd0ad6ba35616286fe95d3670db2b725f3f539673"}, + {file = "rdflib-7.1.3.tar.gz", hash = "sha256:f3dcb4c106a8cd9e060d92f43d593d09ebc3d07adc244f4c7315856a12e383ee"}, ] [package.dependencies] -isodate = "*" -pyparsing = "*" -setuptools = "*" +pyparsing = ">=2.1.0,<4" [package.extras] -berkeleydb = ["berkeleydb"] -dev = ["black (==22.6.0)", "flake8", "flakeheaven", "isort", "mypy", "pep8-naming", "types-setuptools"] -docs = ["myst-parser", "sphinx (<6)", "sphinx-autodoc-typehints", "sphinxcontrib-apidoc", "sphinxcontrib-kroki"] -html = ["html5lib"] -networkx = ["networkx"] -tests = ["html5lib", "pytest", "pytest-cov"] +berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"] +html = ["html5rdf (>=1.2,<2)"] +lxml = ["lxml (>=4.3,<6.0)"] +networkx = ["networkx (>=2,<4)"] +orjson = ["orjson (>=3.9.14,<4)"] [[package]] name = "recipe-scrapers" -version = "15.4.0" +version = "15.5.1" description = "Python package, scraping recipes from all over the internet" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "recipe_scrapers-15.4.0-py3-none-any.whl", hash = "sha256:58d3d60e19d91f43c540496e942bfb4ca244e0de02fe2156edd552226477634f"}, - {file = "recipe_scrapers-15.4.0.tar.gz", hash = "sha256:1587016ce651d259bd51ef693277e7584720ce380ebea6a5bf526094f9fe99f7"}, + {file = "recipe_scrapers-15.5.1-py3-none-any.whl", hash = "sha256:e178295e2e57cd33f9624b5527eac5dbf65cdc2e206ee0cdec2619eea789a462"}, + {file = "recipe_scrapers-15.5.1.tar.gz", hash = "sha256:40c4681210cfe77b2c6d9f1c48f28fdfeadad7323db5c2c7631f3976202c1f6e"}, ] [package.dependencies] @@ -2676,6 +2780,7 @@ version = "2022.10.31" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "regex-2022.10.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8ff454ef0bb061e37df03557afda9d785c905dab15584860f982e88be73015f"}, {file = "regex-2022.10.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eba476b1b242620c266edf6325b443a2e22b633217a9835a52d8da2b5c051f9"}, @@ -2773,6 +2878,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2794,6 +2900,7 @@ version = "1.3.1" description = "OAuthlib authentication support for Requests." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, @@ -2812,6 +2919,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -2826,29 +2934,30 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.9.4" +version = "0.9.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706"}, - {file = "ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf"}, - {file = "ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0"}, - {file = "ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402"}, - {file = "ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e"}, - {file = "ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41"}, - {file = "ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7"}, + {file = "ruff-0.9.7-py3-none-linux_armv6l.whl", hash = "sha256:99d50def47305fe6f233eb8dabfd60047578ca87c9dcb235c9723ab1175180f4"}, + {file = "ruff-0.9.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d59105ae9c44152c3d40a9c40d6331a7acd1cdf5ef404fbe31178a77b174ea66"}, + {file = "ruff-0.9.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f313b5800483770bd540cddac7c90fc46f895f427b7820f18fe1822697f1fec9"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042ae32b41343888f59c0a4148f103208bf6b21c90118d51dc93a68366f4e903"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87862589373b33cc484b10831004e5e5ec47dc10d2b41ba770e837d4f429d721"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a17e1e01bee0926d351a1ee9bc15c445beae888f90069a6192a07a84af544b6b"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c1f880ac5b2cbebd58b8ebde57069a374865c73f3bf41f05fe7a179c1c8ef22"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e63fc20143c291cab2841dbb8260e96bafbe1ba13fd3d60d28be2c71e312da49"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91ff963baed3e9a6a4eba2a02f4ca8eaa6eba1cc0521aec0987da8d62f53cbef"}, + {file = "ruff-0.9.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88362e3227c82f63eaebf0b2eff5b88990280fb1ecf7105523883ba8c3aaf6fb"}, + {file = "ruff-0.9.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0372c5a90349f00212270421fe91874b866fd3626eb3b397ede06cd385f6f7e0"}, + {file = "ruff-0.9.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d76b8ab60e99e6424cd9d3d923274a1324aefce04f8ea537136b8398bbae0a62"}, + {file = "ruff-0.9.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0c439bdfc8983e1336577f00e09a4e7a78944fe01e4ea7fe616d00c3ec69a3d0"}, + {file = "ruff-0.9.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:115d1f15e8fdd445a7b4dc9a30abae22de3f6bcabeb503964904471691ef7606"}, + {file = "ruff-0.9.7-py3-none-win32.whl", hash = "sha256:e9ece95b7de5923cbf38893f066ed2872be2f2f477ba94f826c8defdd6ec6b7d"}, + {file = "ruff-0.9.7-py3-none-win_amd64.whl", hash = "sha256:3770fe52b9d691a15f0b87ada29c45324b2ace8f01200fb0c14845e499eb0c2c"}, + {file = "ruff-0.9.7-py3-none-win_arm64.whl", hash = "sha256:b075a700b2533feb7a01130ff656a4ec0d5f340bb540ad98759b8401c32c2037"}, + {file = "ruff-0.9.7.tar.gz", hash = "sha256:643757633417907510157b206e490c3aa11cab0c087c912f60e07fbafa87a4c6"}, ] [[package]] @@ -2857,6 +2966,7 @@ version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, @@ -2877,6 +2987,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main", "dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2888,6 +2999,7 @@ version = "1.3.0" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, @@ -2899,6 +3011,7 @@ version = "2.3.2.post1" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, @@ -2906,68 +3019,69 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.37" +version = "2.0.38" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-win32.whl", hash = "sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-win_amd64.whl", hash = "sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-win32.whl", hash = "sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-win_amd64.whl", hash = "sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b"}, - {file = "SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1"}, - {file = "sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5e1d9e429028ce04f187a9f522818386c8b076723cdbe9345708384f49ebcec6"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b87a90f14c68c925817423b0424381f0e16d80fc9a1a1046ef202ab25b19a444"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:402c2316d95ed90d3d3c25ad0390afa52f4d2c56b348f212aa9c8d072a40eee5"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6493bc0eacdbb2c0f0d260d8988e943fee06089cd239bd7f3d0c45d1657a70e2"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0561832b04c6071bac3aad45b0d3bb6d2c4f46a8409f0a7a9c9fa6673b41bc03"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:49aa2cdd1e88adb1617c672a09bf4ebf2f05c9448c6dbeba096a3aeeb9d4d443"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-win32.whl", hash = "sha256:64aa8934200e222f72fcfd82ee71c0130a9c07d5725af6fe6e919017d095b297"}, + {file = "SQLAlchemy-2.0.38-cp310-cp310-win_amd64.whl", hash = "sha256:c57b8e0841f3fce7b703530ed70c7c36269c6d180ea2e02e36b34cb7288c50c7"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf89e0e4a30714b357f5d46b6f20e0099d38b30d45fa68ea48589faf5f12f62d"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8455aa60da49cb112df62b4721bd8ad3654a3a02b9452c783e651637a1f21fa2"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53c0d6a859b2db58332e0e6a921582a02c1677cc93d4cbb36fdf49709b327b2"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c4817dff8cef5697f5afe5fec6bc1783994d55a68391be24cb7d80d2dbc3a6"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9cea5b756173bb86e2235f2f871b406a9b9d722417ae31e5391ccaef5348f2c"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e9cdbd18c1f84631312b64993f7d755d85a3930252f6276a77432a2b25a2f3"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-win32.whl", hash = "sha256:cb39ed598aaf102251483f3e4675c5dd6b289c8142210ef76ba24aae0a8f8aba"}, + {file = "SQLAlchemy-2.0.38-cp311-cp311-win_amd64.whl", hash = "sha256:f9d57f1b3061b3e21476b0ad5f0397b112b94ace21d1f439f2db472e568178ae"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-win32.whl", hash = "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725"}, + {file = "SQLAlchemy-2.0.38-cp312-cp312-win_amd64.whl", hash = "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120"}, + {file = "SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:40310db77a55512a18827488e592965d3dec6a3f1e3d8af3f8243134029daca3"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d3043375dd5bbcb2282894cbb12e6c559654c67b5fffb462fda815a55bf93f7"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70065dfabf023b155a9c2a18f573e47e6ca709b9e8619b2e04c54d5bcf193178"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c058b84c3b24812c859300f3b5abf300daa34df20d4d4f42e9652a4d1c48c8a4"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0398361acebb42975deb747a824b5188817d32b5c8f8aba767d51ad0cc7bb08d"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-win32.whl", hash = "sha256:a2bc4e49e8329f3283d99840c136ff2cd1a29e49b5624a46a290f04dff48e079"}, + {file = "SQLAlchemy-2.0.38-cp37-cp37m-win_amd64.whl", hash = "sha256:9cd136184dd5f58892f24001cdce986f5d7e96059d004118d5410671579834a4"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:665255e7aae5f38237b3a6eae49d2358d83a59f39ac21036413fab5d1e810578"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:92f99f2623ff16bd4aaf786ccde759c1f676d39c7bf2855eb0b540e1ac4530c8"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa498d1392216fae47eaf10c593e06c34476ced9549657fca713d0d1ba5f7248"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9afbc3909d0274d6ac8ec891e30210563b2c8bdd52ebbda14146354e7a69373"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:57dd41ba32430cbcc812041d4de8d2ca4651aeefad2626921ae2a23deb8cd6ff"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3e35d5565b35b66905b79ca4ae85840a8d40d31e0b3e2990f2e7692071b179ca"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-win32.whl", hash = "sha256:f0d3de936b192980209d7b5149e3c98977c3810d401482d05fb6d668d53c1c63"}, + {file = "SQLAlchemy-2.0.38-cp38-cp38-win_amd64.whl", hash = "sha256:3868acb639c136d98107c9096303d2d8e5da2880f7706f9f8c06a7f961961149"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07258341402a718f166618470cde0c34e4cec85a39767dce4e24f61ba5e667ea"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a826f21848632add58bef4f755a33d45105d25656a0c849f2dc2df1c71f6f50"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:386b7d136919bb66ced64d2228b92d66140de5fefb3c7df6bd79069a269a7b06"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f2951dc4b4f990a4b394d6b382accb33141d4d3bd3ef4e2b27287135d6bdd68"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bf312ed8ac096d674c6aa9131b249093c1b37c35db6a967daa4c84746bc1bc9"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6db316d6e340f862ec059dc12e395d71f39746a20503b124edc255973977b728"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-win32.whl", hash = "sha256:c09a6ea87658695e527104cf857c70f79f14e9484605e205217aae0ec27b45fc"}, + {file = "SQLAlchemy-2.0.38-cp39-cp39-win_amd64.whl", hash = "sha256:12f5c9ed53334c3ce719155424dc5407aaa4f6cadeb09c5b627e06abb93933a1"}, + {file = "SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753"}, + {file = "sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb"}, ] [package.dependencies] @@ -3005,6 +3119,7 @@ version = "0.41.0" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "starlette-0.41.0-py3-none-any.whl", hash = "sha256:a0193a3c413ebc9c78bff1c3546a45bb8c8bcb4a84cae8747d650a65bd37210a"}, {file = "starlette-0.41.0.tar.gz", hash = "sha256:39cbd8768b107d68bfe1ff1672b38a2c38b49777de46d2a592841d58e3bf7c2a"}, @@ -3022,6 +3137,7 @@ version = "1.3" description = "The most basic Text::Unidecode port" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, @@ -3033,6 +3149,7 @@ version = "0.11.6" description = "Style preserving TOML library" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, @@ -3044,6 +3161,7 @@ version = "4.66.4" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, @@ -3064,6 +3182,7 @@ version = "2.8.19.6" description = "Typing stubs for python-dateutil" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-python-dateutil-2.8.19.6.tar.gz", hash = "sha256:4a6f4cc19ce4ba1a08670871e297bf3802f55d4f129e6aa2443f540b6cf803d2"}, {file = "types_python_dateutil-2.8.19.6-py3-none-any.whl", hash = "sha256:cfb7d31021c6bce6f3362c69af6e3abb48fe3e08854f02487e844ff910deec2a"}, @@ -3075,6 +3194,7 @@ version = "6.1.0.1" description = "Typing stubs for python-slugify" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-python-slugify-6.1.0.1.tar.gz", hash = "sha256:21fd3af49842944acf96f07e320399fac51fdb9c857b7bf1cf2118526e252a22"}, {file = "types_python_slugify-6.1.0.1-py3-none-any.whl", hash = "sha256:260d08ab291d15fa7ab26b95bfb23eddb422aaeb9b9182d0da46decbf751349e"}, @@ -3086,6 +3206,7 @@ version = "6.0.12.4" description = "Typing stubs for PyYAML" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-PyYAML-6.0.12.4.tar.gz", hash = "sha256:ade6e328a5a3df816c47c912c2e1e946ae2bace90744aa73111ee6834b03a314"}, {file = "types_PyYAML-6.0.12.4-py3-none-any.whl", hash = "sha256:de3bacfc4e0772d9b1baf007c37354f3c34c8952e90307d5155b6de0fc183a67"}, @@ -3097,6 +3218,7 @@ version = "2.28.11.8" description = "Typing stubs for requests" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-requests-2.28.11.8.tar.gz", hash = "sha256:e67424525f84adfbeab7268a159d3c633862dafae15c5b19547ce1b55954f0a3"}, {file = "types_requests-2.28.11.8-py3-none-any.whl", hash = "sha256:61960554baca0008ae7e2db2bd3b322ca9a144d3e80ce270f5fb640817e40994"}, @@ -3111,6 +3233,7 @@ version = "1.26.25.4" description = "Typing stubs for urllib3" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-urllib3-1.26.25.4.tar.gz", hash = "sha256:eec5556428eec862b1ac578fb69aab3877995a99ffec9e5a12cf7fbd0cc9daee"}, {file = "types_urllib3-1.26.25.4-py3-none-any.whl", hash = "sha256:ed6b9e8a8be488796f72306889a06a3fc3cb1aa99af02ab8afb50144d7317e49"}, @@ -3122,6 +3245,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -3133,6 +3257,7 @@ version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] files = [ {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, @@ -3144,6 +3269,7 @@ version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +groups = ["main", "dev"] files = [ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, @@ -3160,6 +3286,7 @@ version = "0.34.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, @@ -3185,6 +3312,8 @@ version = "0.20.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = false python-versions = ">=3.8.0" +groups = ["main"] +markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\"" files = [ {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9ebafa0b96c62881d5cafa02d9da2e44c23f9f0cd829f3a32a6aff771449c996"}, {file = "uvloop-0.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:35968fc697b0527a06e134999eef859b4034b37aebca537daeb598b9d45a137b"}, @@ -3229,6 +3358,7 @@ version = "20.28.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, @@ -3249,6 +3379,7 @@ version = "2.1.1" description = "Library of web-related functions" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "w3lib-2.1.1-py3-none-any.whl", hash = "sha256:7fd5bd7980a95d1a8185e867d05f68a591aa281a3ded4590d2641d7b09086ed4"}, {file = "w3lib-2.1.1.tar.gz", hash = "sha256:0e1198f1b745195b6b3dd1a4cd66011fbf82f30a4d9dabaee1f9e5c86f020274"}, @@ -3260,6 +3391,7 @@ version = "2.2.1" description = "Filesystem events monitoring" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260"}, {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5100eae58133355d3ca6c1083a33b81355c4f452afa474c2633bd2fbbba398b3"}, @@ -3300,6 +3432,7 @@ version = "0.18.1" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "watchfiles-0.18.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9891d3c94272108bcecf5597a592e61105279def1313521e637f2d5acbe08bc9"}, {file = "watchfiles-0.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7102342d60207fa635e24c02a51c6628bf0472e5fef067f78a612386840407fc"}, @@ -3330,6 +3463,7 @@ version = "0.5.1" description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, @@ -3341,6 +3475,7 @@ version = "10.4" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, @@ -3417,6 +3552,6 @@ files = [ pgsql = ["psycopg2-binary"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" -content-hash = "9b9bb87e23f58b14a1d010aeca8093f52df30d794ee5a5028f853dbb8ba883f6" +content-hash = "3442bd32ecbf82e5d49975511c3b01b0baa877712d9c786b12cfb5dfdda0c08f" diff --git a/pyproject.toml b/pyproject.toml index c23328be3..6447766ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,10 +3,14 @@ authors = ["Hayden "] description = "A Recipe Manager" license = "AGPL" name = "mealie" -version = "2.5.0" +version = "2.6.0" +include = [ + # Explicit include to override .gitignore when packaging the frontend + { path = "mealie/frontend/**/*", format = ["sdist", "wheel"] } +] [tool.poetry.scripts] -start = "mealie.app:main" +mealie = "mealie.main:main" [tool.poetry.dependencies] Jinja2 = "^3.1.2" @@ -47,7 +51,7 @@ paho-mqtt = "^1.6.1" pydantic-settings = "^2.1.0" pillow-heif = "^0.21.0" pyjwt = "^2.8.0" -openai = "^1.27.0" +openai = "^1.63.0" typing-extensions = "^4.12.2" itsdangerous = "^2.2.0" diff --git a/tests/integration_tests/user_recipe_tests/test_recipe_share_tokens.py b/tests/integration_tests/user_recipe_tests/test_recipe_share_tokens.py index 7d4fc6c57..f3adfe29d 100644 --- a/tests/integration_tests/user_recipe_tests/test_recipe_share_tokens.py +++ b/tests/integration_tests/user_recipe_tests/test_recipe_share_tokens.py @@ -1,4 +1,5 @@ from collections.abc import Generator +from datetime import UTC, datetime, timedelta import pytest import sqlalchemy @@ -119,3 +120,52 @@ def test_share_recipe_from_different_group(api_client: TestClient, unique_user: response = api_client.post(api_routes.shared_recipes, json={"recipeId": str(recipe.id)}, headers=g2_user.token) assert response.status_code == 404 + + +def test_share_recipe_from_different_household( + api_client: TestClient, unique_user: TestUser, h2_user: TestUser, slug: str +): + database = unique_user.repos + recipe = database.recipes.get_one(slug) + assert recipe + + response = api_client.post(api_routes.shared_recipes, json={"recipeId": str(recipe.id)}, headers=h2_user.token) + assert response.status_code == 201 + + +def test_get_recipe_from_token(api_client: TestClient, unique_user: TestUser, slug: str): + database = unique_user.repos + recipe = database.recipes.get_one(slug) + assert recipe + + token = database.recipe_share_tokens.create( + RecipeShareTokenSave(recipe_id=recipe.id, group_id=unique_user.group_id) + ) + + response = api_client.get(api_routes.recipes_shared_token_id(token.id)) + assert response.status_code == 200 + + response_data = response.json() + assert response_data["id"] == str(recipe.id) + + +def test_get_recipe_from_expired_token_deletes_token_and_returns_404( + api_client: TestClient, unique_user: TestUser, slug: str +): + database = unique_user.repos + recipe = database.recipes.get_one(slug) + assert recipe + + token = database.recipe_share_tokens.create( + RecipeShareTokenSave( + recipe_id=recipe.id, group_id=unique_user.group_id, expiresAt=datetime.now(UTC) - timedelta(minutes=1) + ) + ) + fetch_token = database.recipe_share_tokens.get_one(token.id) + assert fetch_token + + response = api_client.get(api_routes.recipes_shared_token_id(token.id), headers=unique_user.token) + assert response.status_code == 404 + + fetch_token = database.recipe_share_tokens.get_one(token.id) + assert fetch_token is None diff --git a/tests/unit_tests/services_tests/scheduler/tasks/test_purge_expired_share_tokens.py b/tests/unit_tests/services_tests/scheduler/tasks/test_purge_expired_share_tokens.py new file mode 100644 index 000000000..2273d3ef7 --- /dev/null +++ b/tests/unit_tests/services_tests/scheduler/tasks/test_purge_expired_share_tokens.py @@ -0,0 +1,38 @@ +from datetime import UTC, datetime, timedelta + +from mealie.schema.recipe.recipe import Recipe +from mealie.schema.recipe.recipe_share_token import RecipeShareTokenSave +from mealie.services.scheduler.tasks.purge_expired_share_tokens import purge_expired_tokens +from tests.utils.factories import random_string +from tests.utils.fixture_schemas import TestUser + + +def test_no_expired_tokens(): + # make sure this task runs successfully even if there are no expired tokens + purge_expired_tokens() + + +def test_delete_expired_tokens(unique_user: TestUser): + db = unique_user.repos + recipe = db.recipes.create( + Recipe(user_id=unique_user.user_id, group_id=unique_user.group_id, name=random_string(20)) + ) + assert recipe and recipe.id + good_token = db.recipe_share_tokens.create( + RecipeShareTokenSave( + recipe_id=recipe.id, group_id=unique_user.group_id, expires_at=datetime.now(UTC) + timedelta(hours=1) + ) + ) + bad_token = db.recipe_share_tokens.create( + RecipeShareTokenSave( + recipe_id=recipe.id, group_id=unique_user.group_id, expires_at=datetime.now(UTC) - timedelta(hours=1) + ) + ) + + assert db.recipe_share_tokens.get_one(good_token.id) + assert db.recipe_share_tokens.get_one(bad_token.id) + + purge_expired_tokens() + + assert db.recipe_share_tokens.get_one(good_token.id) + assert not db.recipe_share_tokens.get_one(bad_token.id)