From 2c4b1d130e452167e1c75839088b593b6639d196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 23 Sep 2024 21:27:11 +0500 Subject: [PATCH 001/126] test prod workflow --- .github/workflows/production_deploy.yaml | 95 ++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/production_deploy.yaml diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml new file mode 100644 index 00000000..617e7afd --- /dev/null +++ b/.github/workflows/production_deploy.yaml @@ -0,0 +1,95 @@ +name: Production deploy + +on: + push: + branches: + - feature/convert_ci-cd_to_the_prod + +env: + REGISTRY: ghcr.io + IMAGE_NAME: adaptive_hockey_federation + DEPLOY_PATH: adaptive_hockey_federation + +defaults: + run: + working-directory: . + +jobs: + code_style_pep8: + runs-on: ubuntu-latest + name: ruff + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Установка зависимостей + run: | + poetry install + + - name: ruff + run: | + poetry run ruff check + pytest: + needs: code_style_pep8 + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + build_and_push: + needs: [code_style_pep8, pytest] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + if: github.ref == 'refs/heads/master' + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file From 0bfa42ea056f21c08a6a71ba3574771b53ffbcc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 20:50:01 +0500 Subject: [PATCH 002/126] fix /infra/prod/ --- infra/prod/adaptive_hockey_federation.service | 31 +++++++++++++++++++ infra/prod/docker-compose.prod.yaml | 8 ++--- 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 infra/prod/adaptive_hockey_federation.service diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service new file mode 100644 index 00000000..0efc8685 --- /dev/null +++ b/infra/prod/adaptive_hockey_federation.service @@ -0,0 +1,31 @@ +[Unit] + +Description=adaptive_hockey_federation +Requires=docker.service +After=docker.service + +[Service] + +Restart=always +RestartSec=5 +TimeOutStartSec=1200 +User=production + +WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ + +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull + +# compose up +ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env up + +# Call when daemon allready stop +ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down + +# Call when daemon already start +ExecStartPost=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env ps + + +[Install] + +WantedBy=multi-user.target \ No newline at end of file diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 3b1bc675..64236b3c 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -9,16 +9,16 @@ services: volumes: - postgres_data:/var/lib/postgresql/data/ env_file: - - ./.env + - ../../.env site: - image: imageName + image: "${IMAGE_COMPOSE}" restart: always volumes: - static_value:/app/static/ - media_value:/app/media/ env_file: - - ./.env + - ../../.env depends_on: - db @@ -27,7 +27,7 @@ services: ports: - "80:80" volumes: - - ../../nginx/default.conf:/etc/nginx/conf.d/default.conf + - ../nginx/nginx_prod.conf:/etc/nginx/conf.d/default.conf - static_value:/var/html/static/ - media_value:/var/html/media/ depends_on: From ff24d4e7245ae572a50db9874c99f49b102303c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 20:54:46 +0500 Subject: [PATCH 003/126] fix docker compose prod --- .github/workflows/production_deploy.yaml | 89 +++++++++++++++++++++++- infra/nginx/nginx_prod.conf | 34 +++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 infra/nginx/nginx_prod.conf diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 617e7afd..554eb990 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -85,11 +85,96 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/master' uses: docker/build-push-action@v5 with: context: . file: infra/prod/prod.Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} + + deploy: + name: Deploy changes on server + runs-on: ubuntu-latest + environment: + name: stage_deploy + needs: [pytest, code_style_pep8, build_and_push] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: dev + + - name: Set up SSH + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + - name: Create folder for deploy + run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + + - name: Copy dev folder to VPS + run: | + scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + + - name: Execute commands on VPS + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.TEST_RSA_SECRET_KEY }} + script: | + cd ${{ env.DEPLOY_PATH }} + rm .env + touch .env + + echo HOST=${{ secrets.HOST }} >> .env + echo PORT=${{ secrets.PORT }} >> .env + echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env + echo ST=${{ secrets.ST }} >> .env + + echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env + echo DEBUG=${{ secrets.DEBUG }} >> .env + echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env + echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env + + echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env + echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env + echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env + echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env + echo DB_HOST=${{ secrets.DB_HOST }} >> .env + echo DB_PORT=${{ secrets.DB_PORT }} >> .env + + echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env + echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env + echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env + echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env + echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env + echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env + + # TODO Добавить копирование переменных с конфигами для Celery и Redis + + cd infra/prod/ + sudo systemctl stop adaptive_hockey_federation.service + docker system prune --force + + # Installing defend service for app + sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + sudo systemctl daemon-reload + sudo systemctl start adaptive_hockey_federation.service + + sudo systemctl is-active --quiet adaptive_hockey_federation.service + until [ $? -eq 0 ]; do + echo "Waiting for adaptive_hockey_federation.service to be active..." + sleep 5 + sudo systemctl is-active --quiet adaptive_hockey_federation.service + done + + echo "adaptive_hockey_federation.service is active" + + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file diff --git a/infra/nginx/nginx_prod.conf b/infra/nginx/nginx_prod.conf new file mode 100644 index 00000000..f5dabf4d --- /dev/null +++ b/infra/nginx/nginx_prod.conf @@ -0,0 +1,34 @@ +server{ + listen 80; + listen [::]:80; + server_name _; + return 308 https://$host$request_uri; +} + +server{ + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name ${HOST}; + include /config/nginx/ssl.conf; + location / { + proxy_pass http://site:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /admin/ { + proxy_pass http://site:8000/admin/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /media/ { + root /var/html/; + } + + location /static/ { + root /var/html/; + } +} \ No newline at end of file From 1cbcf946a78d48fa140b8db9f373ce93d4893b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:16:19 +0500 Subject: [PATCH 004/126] test prod.dockerfile --- infra/prod/entrypoint.sh | 0 infra/prod/prod.Dockerfile | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 infra/prod/entrypoint.sh diff --git a/infra/prod/entrypoint.sh b/infra/prod/entrypoint.sh new file mode 100644 index 00000000..e69de29b diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index 57f048ff..b4a8e8e3 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -11,8 +11,8 @@ FROM python:3.11-slim-bullseye COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh -COPY --from=builder /app /app +# COPY --from=builder /app /app COPY adaptive_hockey_federation/ ./ -ENTRYPOINT ["/entrypoint.sh"] +# ENTRYPOINT ["/entrypoint.sh"] CMD ["/app/.venv/bin/gunicorn", "adaptive_hockey_federation.wsgi:application", "--bind", "0:8000" ] From 9962a93331288c1fae274052ba0a8e4a8af01b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:19:59 +0500 Subject: [PATCH 005/126] test prod.dockerfile2 --- infra/prod/prod.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index b4a8e8e3..ecb636a9 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -9,9 +9,9 @@ RUN python -m pip install --no-cache-dir poetry==1.6.1 \ FROM python:3.11-slim-bullseye -COPY entrypoint.sh /entrypoint.sh +# COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh -# COPY --from=builder /app /app +COPY --from=builder /app /app COPY adaptive_hockey_federation/ ./ # ENTRYPOINT ["/entrypoint.sh"] From f8d457cf5d72719330c754efb7dd476626741120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:22:31 +0500 Subject: [PATCH 006/126] test prod.dockerfile3 --- infra/prod/prod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index ecb636a9..edd942d5 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -10,7 +10,7 @@ RUN python -m pip install --no-cache-dir poetry==1.6.1 \ FROM python:3.11-slim-bullseye # COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh +# RUN chmod +x /entrypoint.sh COPY --from=builder /app /app COPY adaptive_hockey_federation/ ./ # ENTRYPOINT ["/entrypoint.sh"] From 3d4ffa21599ee2cdb981774e468c3c4fcd00c76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:49:07 +0500 Subject: [PATCH 007/126] test prod.dockerfile4 --- .../build-and-push-github-packages.yaml | 1 + .github/workflows/production_deploy.yaml | 170 +++++++++--------- 2 files changed, 90 insertions(+), 81 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index d1d7d827..7cf38c1a 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -5,6 +5,7 @@ on: branches: - master - dev + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 554eb990..a6623846 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -1,9 +1,17 @@ name: Production deploy +# on: +# push: +# branches: +# - feature/convert_ci-cd_to_the_prod + on: - push: - branches: - - feature/convert_ci-cd_to_the_prod + workflow_run: + workflows: + - Build and push Docker image + - pytest + types: + - completed env: REGISTRY: ghcr.io @@ -15,90 +23,90 @@ defaults: working-directory: . jobs: - code_style_pep8: - runs-on: ubuntu-latest - name: ruff - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Установка зависимостей - run: | - poetry install - - - name: ruff - run: | - poetry run ruff check - pytest: - needs: code_style_pep8 - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - build_and_push: - needs: [code_style_pep8, pytest] - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} +# code_style_pep8: +# runs-on: ubuntu-latest +# name: ruff +# steps: +# - name: Install Python +# uses: actions/setup-python@v4 +# with: +# python-version: 3.11 + +# - name: Install Poetry +# uses: snok/install-poetry@v1 +# with: +# poetry-version: 1.5.0 + +# - name: Check out the repo +# uses: actions/checkout@v4 + +# - name: Установка зависимостей +# run: | +# poetry install + +# - name: ruff +# run: | +# poetry run ruff check +# pytest: +# needs: code_style_pep8 +# runs-on: ubuntu-latest +# name: pytest +# steps: +# - name: Install Python +# uses: actions/setup-python@v4 +# with: +# python-version: 3.11 + +# - name: Install Poetry +# uses: snok/install-poetry@v1 +# with: +# poetry-version: 1.5.0 + +# - name: Check out the repo +# uses: actions/checkout@v4 + +# - name: Install dependencies +# run: | +# poetry install +# - name: pytest +# run: | +# poetry run pytest +# working-directory: adaptive_hockey_federation +# build_and_push: +# needs: [code_style_pep8, pytest] +# runs-on: ubuntu-latest + +# steps: +# - uses: actions/checkout@v3 + +# - name: Login to GitHub Container Registry +# uses: docker/login-action@v3 +# with: +# registry: ${{ env.REGISTRY }} +# username: ${{ github.actor }} +# password: ${{ secrets.GITHUB_TOKEN }} + +# - name: Extract metadata for Docker +# id: meta +# uses: docker/metadata-action@v5 +# with: +# images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + +# - name: Build and push Docker image for Production +# uses: docker/build-push-action@v5 +# with: +# context: . +# file: infra/prod/prod.Dockerfile +# push: true +# tags: ${{ steps.meta.outputs.tags }} +# labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest environment: name: stage_deploy - needs: [pytest, code_style_pep8, build_and_push] + # needs: [pytest, code_style_pep8, build_and_push] steps: - name: Checkout repository uses: actions/checkout@v4 From a9abd9b765db82d404e05d01f4f2ea71160de964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:51:53 +0500 Subject: [PATCH 008/126] test prod.dockerfile5 --- .github/workflows/build-and-push-github-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index 7cf38c1a..f88b6f57 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -32,7 +32,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/convert_ci-cd_to_the_prod' uses: docker/build-push-action@v5 with: context: . From 698608b68bcde130bbe1af6b3827d95c4c9b55bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 21:55:07 +0500 Subject: [PATCH 009/126] test prod.dockerfile6 --- .../build-and-push-github-packages.yaml | 2 +- .github/workflows/production_deploy.yaml | 65 +++++++++---------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index f88b6f57..7cf38c1a 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -32,7 +32,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/convert_ci-cd_to_the_prod' + if: github.ref == 'refs/heads/master' uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index a6623846..68144c5b 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -1,21 +1,14 @@ name: Production deploy -# on: -# push: -# branches: -# - feature/convert_ci-cd_to_the_prod - on: - workflow_run: - workflows: - - Build and push Docker image - - pytest - types: - - completed + push: + branches: + - feature/convert_ci-cd_to_the_prod + env: REGISTRY: ghcr.io - IMAGE_NAME: adaptive_hockey_federation + IMAGE_NAME: ${{ github.repository }} DEPLOY_PATH: adaptive_hockey_federation defaults: @@ -72,34 +65,34 @@ jobs: # run: | # poetry run pytest # working-directory: adaptive_hockey_federation -# build_and_push: -# needs: [code_style_pep8, pytest] -# runs-on: ubuntu-latest + build_and_push: + # needs: [code_style_pep8, pytest] + runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v3 + steps: + - uses: actions/checkout@v3 -# - name: Login to GitHub Container Registry -# uses: docker/login-action@v3 -# with: -# registry: ${{ env.REGISTRY }} -# username: ${{ github.actor }} -# password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} -# - name: Extract metadata for Docker -# id: meta -# uses: docker/metadata-action@v5 -# with: -# images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} -# - name: Build and push Docker image for Production -# uses: docker/build-push-action@v5 -# with: -# context: . -# file: infra/prod/prod.Dockerfile -# push: true -# tags: ${{ steps.meta.outputs.tags }} -# labels: ${{ steps.meta.outputs.labels }} + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server From a3c664847d1d2d4855c64b29398c29435e7311b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 22:20:25 +0500 Subject: [PATCH 010/126] test deploy to production --- .github/workflows/production_deploy.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 68144c5b..8a65a9e8 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -98,7 +98,7 @@ jobs: name: Deploy changes on server runs-on: ubuntu-latest environment: - name: stage_deploy + name: prod_deploy # needs: [pytest, code_style_pep8, build_and_push] steps: - name: Checkout repository @@ -119,8 +119,10 @@ jobs: - name: Copy dev folder to VPS run: | - scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/prod/ production@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/nginx/ production@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - name: Execute commands on VPS uses: appleboy/ssh-action@master From f9fec8581f66ab116bfa84f63360f03f8df26c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 22:32:44 +0500 Subject: [PATCH 011/126] test deploy to production2 --- .github/workflows/production_deploy.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 8a65a9e8..4fe4a668 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -115,14 +115,12 @@ jobs: echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p prodiction/${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | - # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/prod/ production@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/nginx/ production@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:prodiction/${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:prodiction/${{ env.DEPLOY_PATH }}/infra/ - name: Execute commands on VPS uses: appleboy/ssh-action@master @@ -131,7 +129,7 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.TEST_RSA_SECRET_KEY }} script: | - cd ${{ env.DEPLOY_PATH }} + cd prodiction/${{ env.DEPLOY_PATH }} rm .env touch .env From f723f6dcd906654b683df6216112e29362417006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 22:40:51 +0500 Subject: [PATCH 012/126] test deploy to production3 --- .github/workflows/stage_deploy.yaml | 258 ++++++++++++++-------------- 1 file changed, 129 insertions(+), 129 deletions(-) diff --git a/.github/workflows/stage_deploy.yaml b/.github/workflows/stage_deploy.yaml index a936f1ab..b0056148 100644 --- a/.github/workflows/stage_deploy.yaml +++ b/.github/workflows/stage_deploy.yaml @@ -1,129 +1,129 @@ -name: Project stage deploy - -on: - workflow_run: - workflows: - - Build and push Docker image - types: - - completed - -env: - REGISTRY: ghcr.io - IMAGE_NAME: adaptive_hockey_federation - DEPLOY_PATH: adaptive_hockey_federation - -defaults: - run: - working-directory: . - -jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - - deploy: - name: Deploy changes on server - runs-on: ubuntu-latest - environment: - name: stage_deploy - needs: pytest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: dev - - - name: Set up SSH - run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - - - name: Copy dev folder to VPS - run: | - scp -r $GITHUB_WORKSPACE/infra/stage/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - - - name: Execute commands on VPS - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.TEST_RSA_SECRET_KEY }} - script: | - cd ${{ env.DEPLOY_PATH }} - rm .env - touch .env - - echo HOST=${{ secrets.HOST }} >> .env - echo PORT=${{ secrets.PORT }} >> .env - echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env - echo ST=${{ secrets.ST }} >> .env - - echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env - echo DEBUG=${{ secrets.DEBUG }} >> .env - echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env - echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env - - echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env - echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env - echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env - echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env - echo DB_HOST=${{ secrets.DB_HOST }} >> .env - echo DB_PORT=${{ secrets.DB_PORT }} >> .env - - echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env - echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env - echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env - echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env - echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env - echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env - - # TODO Добавить копирование переменных с конфигами для Celery и Redis - - cd infra/stage/ - sudo systemctl stop adaptive_hockey_federation.service - docker system prune --force - - # Installing defend service for app - sudo cp -f /home/developer/adaptive_hockey_federation/infra/stage/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - sudo systemctl daemon-reload - sudo systemctl start adaptive_hockey_federation.service - - sudo systemctl is-active --quiet adaptive_hockey_federation.service - until [ $? -eq 0 ]; do - echo "Waiting for adaptive_hockey_federation.service to be active..." - sleep 5 - sudo systemctl is-active --quiet adaptive_hockey_federation.service - done - - echo "adaptive_hockey_federation.service is active" - - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate +# name: Project stage deploy + +# on: +# workflow_run: +# workflows: +# - Build and push Docker image +# types: +# - completed + +# env: +# REGISTRY: ghcr.io +# IMAGE_NAME: adaptive_hockey_federation +# DEPLOY_PATH: adaptive_hockey_federation + +# defaults: +# run: +# working-directory: . + +# jobs: +# pytest: +# runs-on: ubuntu-latest +# name: pytest +# steps: +# - name: Install Python +# uses: actions/setup-python@v4 +# with: +# python-version: 3.11 + +# - name: Install Poetry +# uses: snok/install-poetry@v1 +# with: +# poetry-version: 1.5.0 + +# - name: Check out the repo +# uses: actions/checkout@v4 + +# - name: Install dependencies +# run: | +# poetry install +# - name: pytest +# run: | +# poetry run pytest +# working-directory: adaptive_hockey_federation + +# deploy: +# name: Deploy changes on server +# runs-on: ubuntu-latest +# environment: +# name: stage_deploy +# needs: pytest +# steps: +# - name: Checkout repository +# uses: actions/checkout@v4 +# with: +# ref: dev + +# - name: Set up SSH +# run: | +# mkdir -p ~/.ssh +# chmod 700 ~/.ssh +# ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts +# chmod 644 ~/.ssh/known_hosts +# echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa +# chmod 600 ~/.ssh/id_rsa +# - name: Create folder for deploy +# run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + +# - name: Copy dev folder to VPS +# run: | +# scp -r $GITHUB_WORKSPACE/infra/stage/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ +# scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + +# - name: Execute commands on VPS +# uses: appleboy/ssh-action@master +# with: +# host: ${{ secrets.HOST }} +# username: ${{ secrets.USERNAME }} +# key: ${{ secrets.TEST_RSA_SECRET_KEY }} +# script: | +# cd ${{ env.DEPLOY_PATH }} +# rm .env +# touch .env + +# echo HOST=${{ secrets.HOST }} >> .env +# echo PORT=${{ secrets.PORT }} >> .env +# echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env +# echo ST=${{ secrets.ST }} >> .env + +# echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env +# echo DEBUG=${{ secrets.DEBUG }} >> .env +# echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env +# echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env + +# echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env +# echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env +# echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env +# echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env +# echo DB_HOST=${{ secrets.DB_HOST }} >> .env +# echo DB_PORT=${{ secrets.DB_PORT }} >> .env + +# echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env +# echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env +# echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env +# echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env +# echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env +# echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env + +# # TODO Добавить копирование переменных с конфигами для Celery и Redis + +# cd infra/stage/ +# sudo systemctl stop adaptive_hockey_federation.service +# docker system prune --force + +# # Installing defend service for app +# sudo cp -f /home/developer/adaptive_hockey_federation/infra/stage/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service +# sudo systemctl daemon-reload +# sudo systemctl start adaptive_hockey_federation.service + +# sudo systemctl is-active --quiet adaptive_hockey_federation.service +# until [ $? -eq 0 ]; do +# echo "Waiting for adaptive_hockey_federation.service to be active..." +# sleep 5 +# sudo systemctl is-active --quiet adaptive_hockey_federation.service +# done + +# echo "adaptive_hockey_federation.service is active" + +# docker exec adaptive_hockey_federation python manage.py collectstatic --noinput +# docker exec adaptive_hockey_federation python manage.py migrate From 440bf57648329caf30935607f27ea6e0d4f05e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 22:46:19 +0500 Subject: [PATCH 013/126] test deploy to production4 --- .github/workflows/production_deploy.yaml | 56 ++++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 4fe4a668..5e1e5f51 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -65,34 +65,34 @@ jobs: # run: | # poetry run pytest # working-directory: adaptive_hockey_federation - build_and_push: - # needs: [code_style_pep8, pytest] - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + # build_and_push: + # # needs: [code_style_pep8, pytest] + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # - name: Build and push Docker image for Production + # uses: docker/build-push-action@v5 + # with: + # context: . + # file: infra/prod/prod.Dockerfile + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server From 1930378a54e3359ffd6147b07cffd2085d6392b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 24 Sep 2024 22:51:37 +0500 Subject: [PATCH 014/126] test deploy to production6 --- .github/workflows/production_deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 5e1e5f51..b7bbbd61 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -5,7 +5,6 @@ on: branches: - feature/convert_ci-cd_to_the_prod - env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} From 930453a1120c33e7f30b923112b04b31609b2ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 19:12:11 +0500 Subject: [PATCH 015/126] test deploy to production7 --- .github/workflows/build-and-push-github-packages.yaml | 1 - .github/workflows/production_deploy.yaml | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index 7cf38c1a..d1d7d827 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -5,7 +5,6 @@ on: branches: - master - dev - - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index b7bbbd61..0391c1cc 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,17 +109,17 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/authorized_keys chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p prodiction/${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | - # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:prodiction/${{ env.DEPLOY_PATH }}/infra/ - # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:prodiction/${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - name: Execute commands on VPS uses: appleboy/ssh-action@master From 6c659eb29cf17cc6c6b6eba2f4e8c186414ae57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 19:14:00 +0500 Subject: [PATCH 016/126] test deploy to production8 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 0391c1cc..d2d33cdc 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,7 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/authorized_keys + ssh-keyscan -f ${{ secrets.HOST }} > ~/.ssh/authorized_keys chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From a74d3802ef6acbb8e2a0a30224248ed9cd04be02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 19:54:58 +0500 Subject: [PATCH 017/126] test deploy to production9 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index d2d33cdc..ff2135a3 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,7 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -f ${{ secrets.HOST }} > ~/.ssh/authorized_keys + ssh-keyscan -H production/${{ secrets.HOST }} > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From d3df132e10e28f92a1e837a589c67db453f8bf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 19:57:45 +0500 Subject: [PATCH 018/126] test deploy to production10 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index ff2135a3..3054579d 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,7 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H production/${{ secrets.HOST }} > ~/.ssh/known_hosts + ssh-keyscan -H production@${{ secrets.HOST }} > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From 6f2c7f9c3059cfe5324b7e46c65fb68483a2a9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 20:00:47 +0500 Subject: [PATCH 019/126] test deploy to production10 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 3054579d..3d89e02d 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,7 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H production@${{ secrets.HOST }} > ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.USERNAME }}@${{ secrets.HOST }} > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From 7b04894a0b855364008b080747df77e124264c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 20:24:33 +0500 Subject: [PATCH 020/126] test deploy to production11 --- .github/workflows/production_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 3d89e02d..b3b72a41 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -103,13 +103,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: dev + ref: feature/convert_ci-cd_to_the_prod - name: Set up SSH run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.USERNAME }}@${{ secrets.HOST }} > ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From d7c9c22c3456e04f335d8d38358660e4c01d030c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 20:28:29 +0500 Subject: [PATCH 021/126] test deploy to production12 --- .github/workflows/production_deploy.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index b3b72a41..5e855256 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,10 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts + + # Добавляем строку с указанием хоста для сканирования + ssh-keyscan ${{ secrets.HOST }} >> ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From d2ce8b8806b56c8a637aecd56cddc6046e7fcb38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 20:40:24 +0500 Subject: [PATCH 022/126] test deploy to production13 --- .github/workflows/production_deploy.yaml | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 5e855256..e3cfd249 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -105,20 +105,29 @@ jobs: with: ref: feature/convert_ci-cd_to_the_prod - - name: Set up SSH - run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - - # Добавляем строку с указанием хоста для сканирования - ssh-keyscan ${{ secrets.HOST }} >> ~/.ssh/known_hosts + # - name: Set up SSH + # run: | + # mkdir -p ~/.ssh + # chmod 700 ~/.ssh + # ssh-keyscan ${{ secrets.HOST }} >> ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - + # chmod 644 ~/.ssh/known_hosts + # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa + # - name: Create folder for deploy + # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Copy docker-compose.yml via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.TEST_RSA_SECRET_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: "prodiction/${{ env.DEPLOY_PATH }}/" + overwrite: true + strip_components: 1 + - name: Copy dev folder to VPS run: | # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From 4512e646ef0a815a60f397f63a4458643b541f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 20:52:59 +0500 Subject: [PATCH 023/126] test deploy to production14 --- .github/workflows/production_deploy.yaml | 35 ++++++++---------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index e3cfd249..d8a4d85c 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -105,29 +105,18 @@ jobs: with: ref: feature/convert_ci-cd_to_the_prod - # - name: Set up SSH - # run: | - # mkdir -p ~/.ssh - # chmod 700 ~/.ssh - # ssh-keyscan ${{ secrets.HOST }} >> ~/.ssh/known_hosts - - # chmod 644 ~/.ssh/known_hosts - # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - # chmod 600 ~/.ssh/id_rsa - # - name: Create folder for deploy - # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - - name: Copy docker-compose.yml via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.TEST_RSA_SECRET_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/" - target: "prodiction/${{ env.DEPLOY_PATH }}/" - overwrite: true - strip_components: 1 - + - name: Set up SSH + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + + # Указываем параметры команды ssh-keyscan: + ssh-keyscan -H ${{ secrets.HOST }} -t rsa > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + + echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Copy dev folder to VPS run: | # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From 27a3d3546c06eb1e38366ff442b04476a31bd1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 22:32:36 +0500 Subject: [PATCH 024/126] test deploy to production15 --- .github/workflows/production_deploy.yaml | 33 ++++++++++++------------ infra/prod/prod.Dockerfile | 19 +++++--------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index d8a4d85c..0748dfa0 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -93,34 +93,33 @@ jobs: # tags: ${{ steps.meta.outputs.tags }} # labels: ${{ steps.meta.outputs.labels }} - deploy: - name: Deploy changes on server - runs-on: ubuntu-latest - environment: - name: prod_deploy - # needs: [pytest, code_style_pep8, build_and_push] - steps: + deploy: + name: Deploy changes on server + runs-on: ubuntu-latest + environment: + name: prod_deploy + # needs: pytest + steps: - name: Checkout repository uses: actions/checkout@v4 - with: - ref: feature/convert_ci-cd_to_the_prod + # with: + # ref: dev - name: Set up SSH run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - - # Указываем параметры команды ssh-keyscan: - ssh-keyscan -H ${{ secrets.HOST }} -t rsa > ~/.ssh/known_hosts + ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + chmod 600 ~/.ssh/id_rsa + - name: Create folder for deploy + run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | - # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - name: Execute commands on VPS uses: appleboy/ssh-action@master @@ -129,7 +128,7 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.TEST_RSA_SECRET_KEY }} script: | - cd prodiction/${{ env.DEPLOY_PATH }} + cd ${{ env.DEPLOY_PATH }} rm .env touch .env diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index edd942d5..8d5d4cf3 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -1,18 +1,13 @@ -FROM python:3.11-slim-bullseye AS builder +FROM python:3.11 WORKDIR /app -COPY poetry.lock pyproject.toml ./ -RUN python -m pip install --no-cache-dir poetry==1.6.1 \ - && poetry config virtualenvs.in-project true \ - && poetry install --without dev --with test +COPY requirements/production.txt . +RUN pip install -r production.txt --no-cache-dir -FROM python:3.11-slim-bullseye +COPY . . -# COPY entrypoint.sh /entrypoint.sh -# RUN chmod +x /entrypoint.sh -COPY --from=builder /app /app -COPY adaptive_hockey_federation/ ./ -# ENTRYPOINT ["/entrypoint.sh"] +WORKDIR /app/adaptive_hockey_federation -CMD ["/app/.venv/bin/gunicorn", "adaptive_hockey_federation.wsgi:application", "--bind", "0:8000" ] + +CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] From 916dbc932467d0df6e62e2921faa3bca5884befe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 22:49:13 +0500 Subject: [PATCH 025/126] test deploy to production16 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 0748dfa0..596f244b 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,7 +109,7 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts + ssh-keyscan -H jetrai.online > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa From b33a2f17a2342919e03a4f9a53b72798136b2834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 22:52:18 +0500 Subject: [PATCH 026/126] test deploy to production17 --- .github/workflows/production_deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 596f244b..d99b981d 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -114,7 +114,8 @@ jobs: echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | From 520d38a37c6dcedc3a73911788dd90632a756c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 23:10:15 +0500 Subject: [PATCH 027/126] test deploy to production18 --- .github/workflows/production_deploy.yaml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index d99b981d..00c1a13b 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -113,10 +113,20 @@ jobs: chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - - name: Create folder for deploy - # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra - + # - name: Create folder for deploy + # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + # run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Copy docker-compose.yml via ssh + uses: appleboy/scp-action@master + with: + host: jetrai.online + username: production + key: ${{ secrets.TEST_RSA_SECRET_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: ${{ env.DEPLOY_PATH }} + overwrite: true + strip_components: 1 - name: Copy dev folder to VPS run: | scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From d6022d4efc3f3fe64fc250584deeabb8cb80eaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 23:14:38 +0500 Subject: [PATCH 028/126] test deploy to production19 --- .github/workflows/production_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 00c1a13b..1a591bf6 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -111,7 +111,7 @@ jobs: chmod 700 ~/.ssh ssh-keyscan -H jetrai.online > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + echo ${{ secrets.TEST_RSA_SECRET_KEY }} > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # - name: Create folder for deploy # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra From b4d5f6604e5ba420c3a15454a1d9d9a832365aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 23:17:12 +0500 Subject: [PATCH 029/126] test deploy to production20 --- .github/workflows/production_deploy.yaml | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 1a591bf6..8b424a99 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -113,20 +113,20 @@ jobs: chmod 644 ~/.ssh/known_hosts echo ${{ secrets.TEST_RSA_SECRET_KEY }} > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - # - name: Create folder for deploy + - name: Create folder for deploy # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - # run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra - - name: Copy docker-compose.yml via ssh - uses: appleboy/scp-action@master - with: - host: jetrai.online - username: production - key: ${{ secrets.TEST_RSA_SECRET_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/" - target: ${{ env.DEPLOY_PATH }} - overwrite: true - strip_components: 1 + run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra + # - name: Copy docker-compose.yml via ssh + # uses: appleboy/scp-action@master + # with: + # host: jetrai.online + # username: production + # key: ${{ secrets.TEST_RSA_SECRET_KEY }} + # # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/" + # target: ${{ env.DEPLOY_PATH }} + # overwrite: true + # strip_components: 1 - name: Copy dev folder to VPS run: | scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From 176154caf771af2cc155b23bf4b00aa6fd16928a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 23:25:17 +0500 Subject: [PATCH 030/126] test deploy to production21 --- .github/workflows/production_deploy.yaml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 8b424a99..3a806599 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -109,24 +109,14 @@ jobs: run: | mkdir -p ~/.ssh chmod 700 ~/.ssh - ssh-keyscan -H jetrai.online > ~/.ssh/known_hosts + ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - echo ${{ secrets.TEST_RSA_SECRET_KEY }} > ~/.ssh/id_rsa + echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - name: Create folder for deploy # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - run: ssh -vvv production@jetrai.online mkdir -p ${{ env.DEPLOY_PATH }}/infra - # - name: Copy docker-compose.yml via ssh - # uses: appleboy/scp-action@master - # with: - # host: jetrai.online - # username: production - # key: ${{ secrets.TEST_RSA_SECRET_KEY }} - # # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/" - # target: ${{ env.DEPLOY_PATH }} - # overwrite: true - # strip_components: 1 + run: ssh -vvv production@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Copy dev folder to VPS run: | scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From adf55b9a6cfdd326072d531dc13a152121495e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Wed, 25 Sep 2024 23:33:03 +0500 Subject: [PATCH 031/126] test deploy to production22 --- .github/workflows/production_deploy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 3a806599..1ec2c8e1 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -113,6 +113,8 @@ jobs: chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa + - name: Check SSH key + run: ssh-keygen -lf ~/.ssh/id_rsa - name: Create folder for deploy # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra run: ssh -vvv production@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra From 5c63d6d83d90c9593539618c33199d76ac836e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 00:28:15 +0500 Subject: [PATCH 032/126] test deploy to production23 --- .github/workflows/production_deploy.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 1ec2c8e1..9d42e2ea 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -9,6 +9,7 @@ env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} DEPLOY_PATH: adaptive_hockey_federation + USERNAME: production defaults: run: @@ -113,22 +114,23 @@ jobs: chmod 644 ~/.ssh/known_hosts echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa + - name: Check SSH key run: ssh-keygen -lf ~/.ssh/id_rsa + - name: Create folder for deploy - # # run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - run: ssh -vvv production@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | - scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - name: Execute commands on VPS uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} + username: ${{ env.USERNAME }} key: ${{ secrets.TEST_RSA_SECRET_KEY }} script: | cd ${{ env.DEPLOY_PATH }} @@ -159,8 +161,6 @@ jobs: echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env - # TODO Добавить копирование переменных с конфигами для Celery и Redis - cd infra/prod/ sudo systemctl stop adaptive_hockey_federation.service docker system prune --force From 193823f5afc24f3537650be29c76d5dccb9e7d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 07:20:27 +0500 Subject: [PATCH 033/126] test deploy to production24 --- .github/workflows/production_deploy.yaml | 110 +++++++++++------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 9d42e2ea..1375e083 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -126,58 +126,58 @@ jobs: scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - - name: Execute commands on VPS - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ env.USERNAME }} - key: ${{ secrets.TEST_RSA_SECRET_KEY }} - script: | - cd ${{ env.DEPLOY_PATH }} - rm .env - touch .env - - echo HOST=${{ secrets.HOST }} >> .env - echo PORT=${{ secrets.PORT }} >> .env - echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env - echo ST=${{ secrets.ST }} >> .env - - echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env - echo DEBUG=${{ secrets.DEBUG }} >> .env - echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env - echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env - - echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env - echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env - echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env - echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env - echo DB_HOST=${{ secrets.DB_HOST }} >> .env - echo DB_PORT=${{ secrets.DB_PORT }} >> .env - - echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env - echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env - echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env - echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env - echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env - echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env - - cd infra/prod/ - sudo systemctl stop adaptive_hockey_federation.service - docker system prune --force - - # Installing defend service for app - sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - sudo systemctl daemon-reload - sudo systemctl start adaptive_hockey_federation.service - - sudo systemctl is-active --quiet adaptive_hockey_federation.service - until [ $? -eq 0 ]; do - echo "Waiting for adaptive_hockey_federation.service to be active..." - sleep 5 - sudo systemctl is-active --quiet adaptive_hockey_federation.service - done - - echo "adaptive_hockey_federation.service is active" - - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + # - name: Execute commands on VPS + # uses: appleboy/ssh-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ env.USERNAME }} + # key: ${{ secrets.TEST_RSA_SECRET_KEY }} + # script: | + # cd ${{ env.DEPLOY_PATH }} + # rm .env + # touch .env + + # echo HOST=${{ secrets.HOST }} >> .env + # echo PORT=${{ secrets.PORT }} >> .env + # echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env + # echo ST=${{ secrets.ST }} >> .env + + # echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env + # echo DEBUG=${{ secrets.DEBUG }} >> .env + # echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env + # echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env + + # echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env + # echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env + # echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env + # echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env + # echo DB_HOST=${{ secrets.DB_HOST }} >> .env + # echo DB_PORT=${{ secrets.DB_PORT }} >> .env + + # echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env + # echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env + # echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env + # echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env + # echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env + # echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env + + # cd infra/prod/ + # sudo systemctl stop adaptive_hockey_federation.service + # docker system prune --force + + # # Installing defend service for app + # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + # sudo systemctl daemon-reload + # sudo systemctl start adaptive_hockey_federation.service + + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # until [ $? -eq 0 ]; do + # echo "Waiting for adaptive_hockey_federation.service to be active..." + # sleep 5 + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # done + + # echo "adaptive_hockey_federation.service is active" + + # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + # docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file From 54ae2ea9080569222399589f1e58d3c3c14c5a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 07:37:47 +0500 Subject: [PATCH 034/126] test deploy to production25 --- .github/workflows/production_deploy.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index 1375e083..b4c1feb6 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -108,12 +108,15 @@ jobs: - name: Set up SSH run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts + # mkdir -p ~/.ssh + # chmod 700 ~/.ssh + # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + # chmod 644 ~/.ssh/known_hosts + # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa + install -m 600 -D /dev/null ~/.ssh/id_rsa echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - name: Check SSH key run: ssh-keygen -lf ~/.ssh/id_rsa From 5362f84c0364b296639fb3a4938cdf74f736700c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 08:02:16 +0500 Subject: [PATCH 035/126] test deploy to production26 --- .github/workflows/production_deploy.yaml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index b4c1feb6..e9d3438f 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -114,12 +114,21 @@ jobs: # chmod 644 ~/.ssh/known_hosts # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa # chmod 600 ~/.ssh/id_rsa - install -m 600 -D /dev/null ~/.ssh/id_rsa - echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + which ssh-agent || (sudo apk update && sudo apk add openssh-client) + which rsync || (sudo apk update && sudo apk add rsync) + mkdir -p ~/.ssh + chmod 700 ~/.ssh + touch ~/.ssh/private.key + touch ~/.ssh/known_hosts + chmod 600 ~/.ssh/private.key + echo -e "${{ secrets.TEST_RSA_SECRET_KEY }}" | tr -d '\r' > ~/.ssh/private.key + # Append keyscan output into known hosts + ssh-keyscan -H 84.201.161.166 >> ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + - name: Check SSH key - run: ssh-keygen -lf ~/.ssh/id_rsa + run: ssh-keygen -lf ~/.ssh/private.key - name: Create folder for deploy run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra From 29f9fbe2c654bf66d131ec1b964c875817523cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 08:07:11 +0500 Subject: [PATCH 036/126] test deploy to production27 --- .github/workflows/production_deploy.yaml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/production_deploy.yaml index e9d3438f..045bc5ea 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/production_deploy.yaml @@ -105,33 +105,27 @@ jobs: uses: actions/checkout@v4 # with: # ref: dev - + - name: Configure SSH + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.TEST_RSA_SECRET_KEY }} - name: Set up SSH run: | + cd ${{ github.workspace }} + ssh-keyscan -t rsa 84.201.161.166 >> ~/.ssh/known_hosts # mkdir -p ~/.ssh # chmod 700 ~/.ssh # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts # chmod 644 ~/.ssh/known_hosts # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa # chmod 600 ~/.ssh/id_rsa - which ssh-agent || (sudo apk update && sudo apk add openssh-client) - which rsync || (sudo apk update && sudo apk add rsync) - mkdir -p ~/.ssh - chmod 700 ~/.ssh - touch ~/.ssh/private.key - touch ~/.ssh/known_hosts - chmod 600 ~/.ssh/private.key - echo -e "${{ secrets.TEST_RSA_SECRET_KEY }}" | tr -d '\r' > ~/.ssh/private.key - # Append keyscan output into known hosts - ssh-keyscan -H 84.201.161.166 >> ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - - name: Check SSH key - run: ssh-keygen -lf ~/.ssh/private.key + # - name: Check SSH key + # run: ssh-keygen -lf ~/.ssh/private.key - name: Create folder for deploy - run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv ${{ env.USERNAME }}@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra - name: Copy dev folder to VPS run: | From 28ced68b8ecb26fa65ec4e5d90ec105086fdc787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 18:52:05 +0500 Subject: [PATCH 037/126] test secrets --- ...roduction_deploy.yaml => prod_deploy.yaml} | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) rename .github/workflows/{production_deploy.yaml => prod_deploy.yaml} (91%) diff --git a/.github/workflows/production_deploy.yaml b/.github/workflows/prod_deploy.yaml similarity index 91% rename from .github/workflows/production_deploy.yaml rename to .github/workflows/prod_deploy.yaml index 045bc5ea..c139e534 100644 --- a/.github/workflows/production_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,22 +103,15 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - # with: - # ref: dev - - name: Configure SSH - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: ${{ secrets.TEST_RSA_SECRET_KEY }} + - name: Set up SSH run: | - cd ${{ github.workspace }} - ssh-keyscan -t rsa 84.201.161.166 >> ~/.ssh/known_hosts - # mkdir -p ~/.ssh - # chmod 700 ~/.ssh - # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - # chmod 644 ~/.ssh/known_hosts - # echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa - # chmod 600 ~/.ssh/id_rsa + mkdir -p ~/.ssh + chmod 700 ~/.ssh + ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo "${{ secrets.PROD_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa # - name: Check SSH key From 2f559ff6362a3a5331144926752526b439f53f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 19:02:54 +0500 Subject: [PATCH 038/126] test secrets1 --- .github/workflows/prod_deploy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index c139e534..ec229215 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -120,10 +120,10 @@ jobs: - name: Create folder for deploy run: ssh -vvv ${{ env.USERNAME }}@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra - - name: Copy dev folder to VPS - run: | - scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # - name: Copy dev folder to VPS + # run: | + # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ + # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ # - name: Execute commands on VPS # uses: appleboy/ssh-action@master From 53be90165d0b2f7806c44f40b507cb69c18bff2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 19:13:15 +0500 Subject: [PATCH 039/126] test secrets2 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index ec229215..62c4e3ce 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -114,8 +114,8 @@ jobs: chmod 600 ~/.ssh/id_rsa - # - name: Check SSH key - # run: ssh-keygen -lf ~/.ssh/private.key + - name: Check SSH key + run: ssh-keygen -lf ~/.ssh/private.key - name: Create folder for deploy run: ssh -vvv ${{ env.USERNAME }}@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra From 783d553a505f2c43e5c89c95c2ce340c44e55669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 19:49:36 +0500 Subject: [PATCH 040/126] test secrets3 --- .github/workflows/prod_deploy.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 62c4e3ce..9b9e7e4d 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -110,10 +110,9 @@ jobs: chmod 700 ~/.ssh ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.PROD_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - - name: Check SSH key run: ssh-keygen -lf ~/.ssh/private.key From bffe6d79ee5aac6a89e70b94572ed4ad538c6fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 19:50:36 +0500 Subject: [PATCH 041/126] test secrets4 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 9b9e7e4d..5a214680 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -114,7 +114,7 @@ jobs: chmod 600 ~/.ssh/id_rsa - name: Check SSH key - run: ssh-keygen -lf ~/.ssh/private.key + run: ssh-keygen -lf ~/.ssh/id_rsa - name: Create folder for deploy run: ssh -vvv ${{ env.USERNAME }}@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra From 0ad868204a5241798b660da60817d635f373ca62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 20:06:24 +0500 Subject: [PATCH 042/126] test secrets5 --- .github/workflows/prod_deploy.yaml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 5a214680..a1900068 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -106,18 +106,21 @@ jobs: - name: Set up SSH run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts + # mkdir -p ~/.ssh + # chmod 700 ~/.ssh + # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + # chmod 644 ~/.ssh/known_hosts + # echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa + install -m 600 -D /dev/null ~/.ssh/id_rsa echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts - - name: Check SSH key - run: ssh-keygen -lf ~/.ssh/id_rsa + # - name: Check SSH key + # run: ssh-keygen -lf ~/.ssh/id_rsa - name: Create folder for deploy - run: ssh -vvv ${{ env.USERNAME }}@84.201.161.166 mkdir -p ${{ env.DEPLOY_PATH }}/infra + run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra # - name: Copy dev folder to VPS # run: | From 53ae4e1ab00f283ceec50658e5d4e1c639ad8c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 21:37:38 +0500 Subject: [PATCH 043/126] test secrets6 --- .github/workflows/prod_deploy.yaml | 35 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index a1900068..41a08754 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -104,23 +104,32 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up SSH - run: | - # mkdir -p ~/.ssh - # chmod 700 ~/.ssh - # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - # chmod 644 ~/.ssh/known_hosts - # echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - # chmod 600 ~/.ssh/id_rsa - install -m 600 -D /dev/null ~/.ssh/id_rsa - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts + - name: Copy docker-compose.yml via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: ${{ env.DEPLOY_PATH }} + overwrite: true + strip_components: 1 + + # - name: Set up SSH + # run: | + # mkdir -p ~/.ssh + # chmod 700 ~/.ssh + # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + # chmod 644 ~/.ssh/known_hosts + # echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa # - name: Check SSH key # run: ssh-keygen -lf ~/.ssh/id_rsa - - name: Create folder for deploy - run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + # - name: Create folder for deploy + # run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra # - name: Copy dev folder to VPS # run: | From 73ed84a4b4f0865891c07deaff29b12e466e8f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 21:43:57 +0500 Subject: [PATCH 044/126] test secrets7 --- .github/workflows/prod_deploy.yaml | 45 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 41a08754..281f7c4c 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -9,7 +9,6 @@ env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} DEPLOY_PATH: adaptive_hockey_federation - USERNAME: production defaults: run: @@ -104,32 +103,32 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Copy docker-compose.yml via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/" - target: ${{ env.DEPLOY_PATH }} - overwrite: true - strip_components: 1 - - # - name: Set up SSH - # run: | - # mkdir -p ~/.ssh - # chmod 700 ~/.ssh - # ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - # chmod 644 ~/.ssh/known_hosts - # echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - # chmod 600 ~/.ssh/id_rsa + # - name: Copy docker-compose.yml via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/" + # target: ${{ env.DEPLOY_PATH }} + # overwrite: true + # strip_components: 1 + + - name: Set up SSH + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa # - name: Check SSH key # run: ssh-keygen -lf ~/.ssh/id_rsa - # - name: Create folder for deploy - # run: ssh -vvv ${{ env.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Create folder for deploy + run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra # - name: Copy dev folder to VPS # run: | From 5c0591369e163e6ad73a771dca834c7ca7532470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:11:52 +0500 Subject: [PATCH 045/126] test copy infra to vps --- .github/workflows/prod_deploy.yaml | 50 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 281f7c4c..3948d3ca 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,32 +103,30 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - # - name: Copy docker-compose.yml via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/" - # target: ${{ env.DEPLOY_PATH }} - # overwrite: true - # strip_components: 1 - - - name: Set up SSH - run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -H 84.201.161.166 > ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - # - name: Check SSH key - # run: ssh-keygen -lf ~/.ssh/id_rsa - - - name: Create folder for deploy - run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra + - name: Copy infra via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/prod" + target: ${{ env.DEPLOY_PATH }}/infra + overwrite: true + strip_components: 1 + - name: Copy nginx via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/nginx" + target: ${{ env.DEPLOY_PATH }}/infra + overwrite: true + strip_components: 1 + + # - name: Copy dev folder to VPS # run: | From d4c76087678a8591adbecd16acfee7d2643e7aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:17:08 +0500 Subject: [PATCH 046/126] test copy infra to vps - 1 --- .github/workflows/prod_deploy.yaml | 6 +++--- infra/prod/entrypoint.sh | 0 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 infra/prod/entrypoint.sh diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 3948d3ca..f70a1284 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,7 +103,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Copy infra via ssh + - name: Copy prod via ssh uses: appleboy/scp-action@master with: host: ${{ secrets.HOST }} @@ -111,7 +111,7 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/prod" - target: ${{ env.DEPLOY_PATH }}/infra + target: ${{ env.DEPLOY_PATH }}/infra/ overwrite: true strip_components: 1 - name: Copy nginx via ssh @@ -122,7 +122,7 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/nginx" - target: ${{ env.DEPLOY_PATH }}/infra + target: ${{ env.DEPLOY_PATH }}/infra/ overwrite: true strip_components: 1 diff --git a/infra/prod/entrypoint.sh b/infra/prod/entrypoint.sh deleted file mode 100644 index e69de29b..00000000 From 3bd7af305e7947912d7de03b7e2ccbbdfcaa53eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:26:04 +0500 Subject: [PATCH 047/126] test copy infra to vps - 2 --- .github/workflows/prod_deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index f70a1284..367fe183 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -111,7 +111,7 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/prod" - target: ${{ env.DEPLOY_PATH }}/infra/ + target: ${{ env.DEPLOY_PATH }}/infra overwrite: true strip_components: 1 - name: Copy nginx via ssh @@ -120,9 +120,9 @@ jobs: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/nginx" - target: ${{ env.DEPLOY_PATH }}/infra/ + target: ${{ env.DEPLOY_PATH }}/infra overwrite: true strip_components: 1 From f2d68f977b77f80bb92a952b1c03b6c0c83a3802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:29:08 +0500 Subject: [PATCH 048/126] test copy infra to vps - 3 --- .github/workflows/prod_deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 367fe183..e7ed5b6a 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -113,18 +113,18 @@ jobs: source: "infra/prod" target: ${{ env.DEPLOY_PATH }}/infra overwrite: true - strip_components: 1 + # strip_components: 1 - name: Copy nginx via ssh uses: appleboy/scp-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/nginx" target: ${{ env.DEPLOY_PATH }}/infra overwrite: true - strip_components: 1 + # strip_components: 1 From c878c46ce3b3d841ed377397f37106e76f82d3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:36:41 +0500 Subject: [PATCH 049/126] test copy infra to vps - 4 --- .github/workflows/prod_deploy.yaml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index e7ed5b6a..5d5e0cbe 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -110,21 +110,21 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/prod" - target: ${{ env.DEPLOY_PATH }}/infra + source: "./infra/prod/* ./infra/nginx/*" + target: /${{ env.DEPLOY_PATH }}/infra/ overwrite: true - # strip_components: 1 - - name: Copy nginx via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/nginx" - target: ${{ env.DEPLOY_PATH }}/infra - overwrite: true - # strip_components: 1 + # # strip_components: 1 + # - name: Copy nginx via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/nginx" + # target: ${{ env.DEPLOY_PATH }}/infra + # overwrite: true + # # strip_components: 1 From 3df804f277b31d478faa446f1d0ba463acd5e1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:39:03 +0500 Subject: [PATCH 050/126] test copy infra to vps - 5 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 5d5e0cbe..db7de057 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -110,8 +110,8 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "./infra/prod/* ./infra/nginx/*" - target: /${{ env.DEPLOY_PATH }}/infra/ + source: infra/prod infra/nginx + target: ${{ env.DEPLOY_PATH }}/infra overwrite: true # # strip_components: 1 # - name: Copy nginx via ssh From 1a3d8d7a01b32b36a8afbc64de1d3ed5e399759c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:44:24 +0500 Subject: [PATCH 051/126] test copy infra to vps - 6 --- .github/workflows/prod_deploy.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index db7de057..ac69e7bc 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -110,20 +110,20 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: infra/prod infra/nginx - target: ${{ env.DEPLOY_PATH }}/infra + source: "infra/prod" + target: "${{ env.DEPLOY_PATH }}/infra/prod" overwrite: true # # strip_components: 1 - # - name: Copy nginx via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/nginx" - # target: ${{ env.DEPLOY_PATH }}/infra - # overwrite: true + - name: Copy nginx via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/nginx" + target: "${{ env.DEPLOY_PATH }}/infra/nginx" + overwrite: true # # strip_components: 1 From 94033838ecbdcee53811e5b398be73dfa277fbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 22:49:06 +0500 Subject: [PATCH 052/126] test copy infra to vps - 7 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index ac69e7bc..e22a1696 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -111,7 +111,7 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/prod" - target: "${{ env.DEPLOY_PATH }}/infra/prod" + target: "${{ env.DEPLOY_PATH }}/infra" overwrite: true # # strip_components: 1 - name: Copy nginx via ssh @@ -122,7 +122,7 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra/nginx" - target: "${{ env.DEPLOY_PATH }}/infra/nginx" + target: "${{ env.DEPLOY_PATH }}/infra" overwrite: true # # strip_components: 1 From 8f4b9922e3b9ff751d538f3a78a642dc21dfa53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 23:06:31 +0500 Subject: [PATCH 053/126] test copy infra to vps - 8 --- .github/workflows/prod_deploy.yaml | 33 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index e22a1696..7f7f3d90 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,6 +103,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: delete stage & dev + run: | + rm -r infra/stage + rm -r infra/dev + - name: Copy prod via ssh uses: appleboy/scp-action@master with: @@ -110,21 +115,21 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/prod" - target: "${{ env.DEPLOY_PATH }}/infra" - overwrite: true - # # strip_components: 1 - - name: Copy nginx via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/nginx" - target: "${{ env.DEPLOY_PATH }}/infra" + source: "/infra/" + target: "${{ env.DEPLOY_PATH }}" overwrite: true - # # strip_components: 1 + # strip_components: 1 + # - name: Copy nginx via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/nginx" + # target: "${{ env.DEPLOY_PATH }}/infra" + # overwrite: true + # strip_components: 1 From c33f3857111714d7c8536e43daa059c5893982a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 23:09:04 +0500 Subject: [PATCH 054/126] test copy infra to vps - 9 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 7f7f3d90..db46360b 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -115,7 +115,7 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "/infra/" + source: "infra" target: "${{ env.DEPLOY_PATH }}" overwrite: true # strip_components: 1 From a9d883d2f67df23d0207c34306d92ddbe21308ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 23:15:05 +0500 Subject: [PATCH 055/126] test copy infra to vps - 10 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index db46360b..7f7f3d90 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -115,7 +115,7 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra" + source: "/infra/" target: "${{ env.DEPLOY_PATH }}" overwrite: true # strip_components: 1 From c4e23214e44974d7b463cd1667c27305f1e01e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 23:18:40 +0500 Subject: [PATCH 056/126] test copy infra to vps - 11 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 7f7f3d90..b3ead80b 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -115,8 +115,8 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "/infra/" - target: "${{ env.DEPLOY_PATH }}" + source: "infra" + target: "${{ env.DEPLOY_PATH }}/infra" overwrite: true # strip_components: 1 # - name: Copy nginx via ssh From 58acbd17c539546ba2d68cc94d08f97756fb2205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 26 Sep 2024 23:26:02 +0500 Subject: [PATCH 057/126] test copy infra to vps - 12 --- .github/workflows/prod_deploy.yaml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index b3ead80b..7b9643b7 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -116,23 +116,9 @@ jobs: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra" - target: "${{ env.DEPLOY_PATH }}/infra" + target: "${{ env.DEPLOY_PATH }}" overwrite: true - # strip_components: 1 - # - name: Copy nginx via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/nginx" - # target: "${{ env.DEPLOY_PATH }}/infra" - # overwrite: true - # strip_components: 1 - - # - name: Copy dev folder to VPS # run: | # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ From 31d71adf0ac3151eca33345b3e15b757d9a138b9 Mon Sep 17 00:00:00 2001 From: Toksi86 Date: Fri, 27 Sep 2024 16:38:08 +0500 Subject: [PATCH 058/126] Removed the video_api module and everything related to it --- .../core/config/base_settings.py | 1 - .../core/ydisk_utils/__init__.py | 0 .../core/ydisk_utils/utils.py | 104 ----------- adaptive_hockey_federation/games/signals.py | 4 - adaptive_hockey_federation/games/urls.py | 5 - adaptive_hockey_federation/games/views.py | 75 +------- .../main/controllers/player_views.py | 158 +---------------- adaptive_hockey_federation/main/urls.py | 5 - .../service/a_hockey_requests.py | 27 --- .../templates/main/games/game_detail.html | 161 +++++++++--------- .../video_api/__init__.py | 0 adaptive_hockey_federation/video_api/apps.py | 8 - .../video_api/permissions.py | 11 -- .../video_api/serializers.py | 119 ------------- adaptive_hockey_federation/video_api/tasks.py | 150 ---------------- 15 files changed, 82 insertions(+), 746 deletions(-) delete mode 100644 adaptive_hockey_federation/core/ydisk_utils/__init__.py delete mode 100644 adaptive_hockey_federation/core/ydisk_utils/utils.py delete mode 100644 adaptive_hockey_federation/video_api/__init__.py delete mode 100644 adaptive_hockey_federation/video_api/apps.py delete mode 100644 adaptive_hockey_federation/video_api/permissions.py delete mode 100644 adaptive_hockey_federation/video_api/serializers.py delete mode 100644 adaptive_hockey_federation/video_api/tasks.py diff --git a/adaptive_hockey_federation/core/config/base_settings.py b/adaptive_hockey_federation/core/config/base_settings.py index ba753a99..c7302041 100644 --- a/adaptive_hockey_federation/core/config/base_settings.py +++ b/adaptive_hockey_federation/core/config/base_settings.py @@ -41,7 +41,6 @@ "analytics.apps.AnalyticsConfig", "unloads.apps.UnloadsConfig", "games.apps.GamesConfig", - "video_api.apps.VideoApiConfig", ] INSTALLED_APPS = EXTERNAL_APPS + DEFAULT_APPS + LOCAL_APPS diff --git a/adaptive_hockey_federation/core/ydisk_utils/__init__.py b/adaptive_hockey_federation/core/ydisk_utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/adaptive_hockey_federation/core/ydisk_utils/utils.py b/adaptive_hockey_federation/core/ydisk_utils/utils.py deleted file mode 100644 index 091e078d..00000000 --- a/adaptive_hockey_federation/core/ydisk_utils/utils.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -import logging -import sys -from functools import wraps - -import yadisk -from yadisk import Client -from django.conf import settings -from yadisk.exceptions import ( - ForbiddenError, - PathNotFoundError, - ResourceIsLockedError, -) - -from core.constants import YadiskDirectory - - -logger = logging.getLogger(__name__) -logger.setLevel(logging.WARNING) - -console_handler = logging.StreamHandler(sys.stdout) -console_handler.setLevel(logging.WARNING) - -formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s", -) -console_handler.setFormatter(formatter) - -logger.addHandler(console_handler) - - -def yadisk_client(func): - """ - Декоратор для работы с клиентом Yandex Disk. - - Декорируемая функция должна принимать первым аргументом экземпляр - `yadisk.Client`. - - Пример использования: - @yadisk_client - def function(client, *args, **kwargs): - pass - """ - - @wraps(func) - def wrapper(*args, **kwargs): - try: - with yadisk.Client( - token=settings.YANDEX_DISK_OAUTH_TOKEN, - ) as client: - return func(client, *args, **kwargs) - except PathNotFoundError: - error_message = "Файл не найден на Yandex Disk." - logger.exception(error_message) - raise PathNotFoundError(msg=error_message) - except ForbiddenError: - error_message = "Не хватает прав, чтобы выполнить запрос." - logger.exception(error_message) - raise ForbiddenError(msg=error_message) - except ResourceIsLockedError: - error_message = "Файл заблокирован другой операцией." - logger.exception(error_message) - raise ResourceIsLockedError(msg=error_message) - - return wrapper - - -@yadisk_client -def check_file_exists_on_disk(client: Client, file_path: str) -> bool: - """Проверить существование файла на Yandex Disk.""" - return client.exists(file_path) - - -@yadisk_client -def download_file_by_link( - client: Client, - video_link: str, - media_data_path: str, -) -> None: - """Скачать файл с Yandex Disk по ссылке.""" - client.download_public(video_link, media_data_path) - - -def check_player_game_exists_on_disk(player_game_file_name: str) -> bool: - """Проверить существование файла с игроком на Yandex Disk.""" - player_game_path_on_disk = os.path.join( - YadiskDirectory.PLAYER_GAMES, - player_game_file_name, - ) - return check_file_exists_on_disk(player_game_path_on_disk) - - -def download_file_by_link_task( - video_link: str, - media_data_path: str, -): - """Задача для скачивания файла с Yandex Disk.""" - if os.path.exists(media_data_path): - logger.info(f"Файл {media_data_path} уже скачан на диске.") - return - download_file_by_link( - video_link=video_link, - media_data_path=media_data_path, - ) diff --git a/adaptive_hockey_federation/games/signals.py b/adaptive_hockey_federation/games/signals.py index 711b8f98..f24f2631 100644 --- a/adaptive_hockey_federation/games/signals.py +++ b/adaptive_hockey_federation/games/signals.py @@ -34,10 +34,6 @@ def create_game_teams(sender, instance, created, **kwargs): # конкретно изменение # номеров игроков. Оно должно происходить до того # как объект игры попадёт в бд. - # TODO отправлять видео на распознавание до изменения номеров игроков - # нелогично. - # if created and instance.video_link: - # send_game_video_to_process(instance.id) @receiver(post_save, sender=GameTeam, dispatch_uid="unique_signal") diff --git a/adaptive_hockey_federation/games/urls.py b/adaptive_hockey_federation/games/urls.py index a5997fb2..e4b0fea3 100644 --- a/adaptive_hockey_federation/games/urls.py +++ b/adaptive_hockey_federation/games/urls.py @@ -26,11 +26,6 @@ views.GamesInfoView.as_view(), name="game_info", ), - path( - "/process/", - views.send_game_video_to_process_view, - name="send_game_video_to_process_view", - ), ] urlpatterns = [ diff --git a/adaptive_hockey_federation/games/views.py b/adaptive_hockey_federation/games/views.py index 730b7a6a..ad502fb3 100644 --- a/adaptive_hockey_federation/games/views.py +++ b/adaptive_hockey_federation/games/views.py @@ -1,15 +1,12 @@ import logging from dataclasses import dataclass -from requests.exceptions import RequestException + from typing import Any -from django.contrib import messages from django.contrib.auth.mixins import ( LoginRequiredMixin, PermissionRequiredMixin, ) -from django.contrib.auth.decorators import login_required from django.db.models.query import QuerySet -from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.views.generic import DetailView @@ -26,11 +23,7 @@ from games.mixins import GameCreateUpdateMixin from games.models import Game, GamePlayer, GameTeam from core.logging import configure_logging -from service.a_hockey_requests import send_request_to_process_video -from service.a_hockey_requests import check_api_health_status -# TODO раскоментировать после добавления celery -# from video_api.tasks import get_player_video_frames -from video_api.serializers import GameFeatureSerializer + configure_logging() logger = logging.getLogger(__name__) @@ -236,67 +229,3 @@ def get_context_data(self, **kwargs): ) context["page_title"] = "Редактирование номеров игроков команды" return context - - -def send_game_video_to_process( - game_id: int, - user_email: str = None, -) -> Message: - """Функция формирует данные для запроса к серверу DS.""" - game = get_object_or_404(Game, id=game_id) - game_data = GameFeatureSerializer(game).data - kwargs = { - "data": game_data, - "user_email": user_email, - } - try: - check_api_health_status() - except RequestException as error: - message = Message( - messages.ERROR, - "Сервис по обработке видео недоступен", - ) - logger.error(f"Ошибка подключения к серверу распознавания: {error}") - return message - - logger.info("Отправляем видео на обработку") - send_request_to_process_video(kwargs["data"]) - message = Message( - messages.INFO, - "Видео отправлено на обработку, ждите оповещение " - "о готовности на электронную почту.", - ) - return message - - -@login_required -def send_game_video_to_process_view( - request: HttpRequest, - **kwargs: int, -) -> HttpResponseRedirect | HttpResponse: - """ - Вью функция дли запуска задачи в celery для отправки видео на обработку. - - Функция обрабатывает запрос с кнопки, добавляет задачу в очередь и - отображает сообщение об успешном выполнении. - """ - game_id = kwargs.get("game_id") - - if not Game.objects.get(pk=game_id).video_link: - message = Message( - messages.WARNING, - "Ссылка на видео с игрой не указана.", - ) - else: - message = send_game_video_to_process( - game_id=game_id, - user_email=request.user.email, - ) - - messages.add_message( - request, - message.level, - message.text, - ) - - return redirect("games:game_info", game_id=game_id) diff --git a/adaptive_hockey_federation/main/controllers/player_views.py b/adaptive_hockey_federation/main/controllers/player_views.py index 4c145a8a..c8670517 100644 --- a/adaptive_hockey_federation/main/controllers/player_views.py +++ b/adaptive_hockey_federation/main/controllers/player_views.py @@ -1,29 +1,20 @@ -import os from typing import Any -from django.conf import settings -from django.contrib import messages from django.contrib.auth.mixins import ( LoginRequiredMixin, PermissionRequiredMixin, ) -from django.contrib.auth.decorators import login_required from django.http import Http404 -from django.shortcuts import get_object_or_404, render, redirect +from django.shortcuts import get_object_or_404, render from django.urls import reverse, reverse_lazy from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.list import ListView -from requests.exceptions import RequestException -from core.constants import Directory, FileConstants, PLAYER_GAME_NAME +from core.constants import FileConstants from core.utils import is_uploaded_file_valid -from core.ydisk_utils.utils import ( - download_file_by_link_task, - check_player_game_exists_on_disk, -) -from games.models import Game, GamePlayer, GameDataPlayer +from games.models import Game, GamePlayer from main.controllers.mixins import DiagnosisListMixin from main.controllers.utils import errormessage from main.forms import PlayerForm, PlayerUpdateForm @@ -35,10 +26,7 @@ get_player_fields_personal, get_player_table_data, ) -from service.a_hockey_requests import check_api_health_status from unloads.utils import model_get_queryset -from video_api.tasks import create_player_video, get_player_video_frames -from video_api.serializers import GameFeatureSerializer class PlayersListView( @@ -417,146 +405,6 @@ def get_context_data(self, **kwargs) -> dict[str, Any]: return context -@login_required -def unload_player_game_video(request, **kwargs): - """ - Обрабатывает запрос на получение видео с моментами игрока из игры. - - Функция выполняет следующие шаги: - 1. Получает игрока и игру по идентификаторам. - 2. Формирует путь для сохранения видео игры и обработки моментов с игроком. - 3. Проверяет наличие уже существующего видео с моментами игрока на я.диске: - - Если видео уже существует, возвращает ссылку на его скачивание. - - Если видео отсутствует, проверяет наличие фреймов игрока в бд: - - Если фреймы есть, запускает процесс скачивания видео игры, нарезки - моментов с игроком, загрузки видео на я.диск и отправки ссылки - пользователю. - - Если фреймов нет, запускает полный процесс обработки, включая - получение данных с сервера, скачивание видео игры, нарезку моментов, - загрузку видео и отправку ссылки пользователю. - 4. Отправляет сообщение пользователю с информацией о статусе обработки - видео и ссылки на его скачивание. - 5. Перенаправляет пользователя на страницу с видео игрока. - """ - player_id = kwargs["player_id"] - player = get_object_or_404(Player, pk=player_id) - game = get_object_or_404(Game, pk=kwargs["game_id"]) - game_data = GameFeatureSerializer(game).data - player_game_file_name = PLAYER_GAME_NAME.format( - surname=player.surname, - name=player.name, - patronymic=player.patronymic, - game_name=game.name, - ) - - # Директория для скаченных видео с играми. - games_dir = os.path.join( - settings.MEDIA_ROOT, - Directory.GAMES, - ) - os.makedirs(games_dir, exist_ok=True) - game_path = os.path.join( - games_dir, - f"{game.name}.mp4", - ) - - # Директория для обработанных моментов с игроком. - player_games_dir = os.path.join( - settings.MEDIA_ROOT, - Directory.PLAYER_VIDEO_DIR, - ) - os.makedirs(player_games_dir, exist_ok=True) - player_game_frames_path = os.path.join( - player_games_dir, - player_game_file_name, - ) - - if check_player_game_exists_on_disk(player_game_file_name): - # Проверяем есть ли видео с моментами игрока на я.диске. - - # process_chain = chain( - # TODO реализовать таску по отправке ссылки пользователю - # ) - - message_text = "Ссылка для скачивания видео отправлена на почту." - elif GameDataPlayer.objects.filter(player=player, game=game).exists(): - # Проверяем если ли фреймы с игроком в бд. Если есть, то: - - # process_chain = chain( - # download_file_by_link_task.si(game.video_link, game_path).set( - # queue="download_game_video_queue", - # ), - # create_player_video.si( - # game_path, - # player_game_frames_path, - # player.id, - # game.id, - # ).set(queue="slice_player_video_queue"), - # # TODO реализовать таску по загрузке видео с игроком на Я.диск - # # TODO реализовать таску по отправке ссылки пользователю - # ) - - message_text = ( - "Видео находится в обработке. " - "Ссылка для скачивания видео будет отправлена на почту." - ) - else: - # Если нет ни видео, не фреймов, то запускаем полный цикл тасков. - # # TODO реализовать таску по загрузке видео с игроком на Я.диск - # # TODO реализовать таску по отправке ссылки пользователю - try: - download_file_by_link_task(game.video_link, game_path) - except RequestException as e: - messages.add_message( - request, - messages.ERROR, - f"Произошла ошибка при скачивании видео: {e}", - ) - return redirect( - "main:player_id_games_video", - pk=player_id, - ) - try: - check_api_health_status() - except RequestException: - messages.add_message( - request, - messages.ERROR, - "Сервис по обработке видео недоступен", - - ) - return redirect( - "main:player_id_games_video", - pk=player_id, - ) - - frames = get_player_video_frames(game_data) - player_frames = [ - frame["frames"] for frame in frames if frame["number" - ] == player.number] - - create_player_video(input_file=game_path, - output_file=player_game_frames_path, - frames=player_frames[0]) - message_text = ( - "Видео находится в обработке. " - "Ссылка для скачивания видео будет отправлена на почту." - ) - - messages.add_message( - request, - messages.INFO, - message_text, - ) - - # TODO видео будет автоматически загрузаться пользователю по готовности. - # Возможно нужно ресерчить тему WebSockets, SSE - return redirect( - "main:player_id_games_video", - pk=player_id, - ) - - def player_id_deleted(request): """View для отображения информации об успешном удалении игрока.""" return render(request, "main/player_id/player_id_deleted.html") diff --git a/adaptive_hockey_federation/main/urls.py b/adaptive_hockey_federation/main/urls.py index 433c285c..2b72ea5a 100644 --- a/adaptive_hockey_federation/main/urls.py +++ b/adaptive_hockey_federation/main/urls.py @@ -45,11 +45,6 @@ player_views.player_id_deleted, name="player_id_deleted", ), - path( - "/unload/game_video//", - player_views.unload_player_game_video, - name="unload_player_video", - ), ] teams_urlpatterns = [ diff --git a/adaptive_hockey_federation/service/a_hockey_requests.py b/adaptive_hockey_federation/service/a_hockey_requests.py index 3a040f13..c48a5594 100644 --- a/adaptive_hockey_federation/service/a_hockey_requests.py +++ b/adaptive_hockey_federation/service/a_hockey_requests.py @@ -1,5 +1,4 @@ import logging -from typing import Any from urllib.parse import urljoin import requests @@ -24,29 +23,3 @@ def check_api_health_status() -> None: raise RequestException( f"Сервис по обработке видео недоступен: {error}", ) from error - - -def send_request_to_process_video( - data: dict[str, Any], -) -> dict[str, Any]: - """ - Отправка запроса к эндпоинту /process для обработки видео. - - :param data: Словарь с данными для обработки видео. - :returns: Результат обработки видео. - :raises RequestException: Если возникла ошибка при обработке видео. - """ - logger.info("Отправка запроса к серверу DS.") - try: - response = requests.post( - urljoin(settings.PROCESSING_SERVICE_BASE_URL, "/process"), - json=data, - timeout=(0.5, None), - ) - return response.json() - except RequestException as error: - logger.error(f"Ошибка подключения к серверу распознавания: {error}") - return { - "message": "Возникла ошибка при попытке обработать видео: " - "Ошибка подключения к серверу распознавания.", - } diff --git a/adaptive_hockey_federation/templates/main/games/game_detail.html b/adaptive_hockey_federation/templates/main/games/game_detail.html index c85b55f7..4eb755bf 100644 --- a/adaptive_hockey_federation/templates/main/games/game_detail.html +++ b/adaptive_hockey_federation/templates/main/games/game_detail.html @@ -2,96 +2,89 @@ {% load user_filters %} {% block title %} - {{ page_title }} +{{ page_title }} {% endblock title %} {% block content %} - {% include 'base/messages.html' %} -

Детали игры

+{% include 'base/messages.html' %} +

Детали игры

-
-

Название: {{ object.name }}

-
Дата: {{ object.date }}
-
+
+

Название: {{ object.name }}

+
Дата: {{ object.date }}
+
-

Ссылка на видео: {{ object.video_link }}

-

Ссылка на соревнование: {{ object.competition }}

+

Ссылка на видео: {{ object.video_link }}

+

Ссылка на соревнование: {{ object.competition + }}

-
-
-
- {% if teams|length > 0 %} -

{{ teams.0.name }}

- - - - - - - - - {% for player in teams.0.players %} - - - - - {% empty %} - - - - {% endfor %} - -
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
- - Редактировать номера игроков - - {% else %} -

К сожалению, информация о первой команде недоступна.

- {% endif %} -
+
+
+
+ {% if teams|length > 0 %} +

{{ teams.0.name }}

+ + + + + + + + + {% for player in teams.0.players %} + + + + + {% empty %} + + + + {% endfor %} + +
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
+ + Редактировать номера игроков + + {% else %} +

К сожалению, информация о первой команде недоступна.

+ {% endif %} +
-
- {% if teams|length > 1 %} -

{{ teams.1.name }}

- - - - - - - - - {% for player in teams.1.players %} - - - - - {% empty %} - - - - {% endfor %} - -
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
- - Редактировать номера игроков - - {% else %} -

К сожалению, информация о второй команде недоступна.

- {% endif %} -
-
-
-
- - Отправить видео на распознание - +
+ {% if teams|length > 1 %} +

{{ teams.1.name }}

+ + + + + + + + + {% for player in teams.1.players %} + + + + + {% empty %} + + + + {% endfor %} + +
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
+ + Редактировать номера игроков + + {% else %} +

К сожалению, информация о второй команде недоступна.

+ {% endif %} +
+
{% endblock content %} diff --git a/adaptive_hockey_federation/video_api/__init__.py b/adaptive_hockey_federation/video_api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/adaptive_hockey_federation/video_api/apps.py b/adaptive_hockey_federation/video_api/apps.py deleted file mode 100644 index 3afb9217..00000000 --- a/adaptive_hockey_federation/video_api/apps.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.apps import AppConfig - - -class VideoApiConfig(AppConfig): - """Конфигурация приложения Video API.""" - - default_auto_field = "django.db.models.BigAutoField" - name = "video_api" diff --git a/adaptive_hockey_federation/video_api/permissions.py b/adaptive_hockey_federation/video_api/permissions.py deleted file mode 100644 index d1f93e53..00000000 --- a/adaptive_hockey_federation/video_api/permissions.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.conf import settings -from rest_framework.permissions import BasePermission - - -class HasAPIDocsKey(BasePermission): - """Кастомный пермишен для API ключа.""" - - def has_permission(self, request, view): - """Проверка наличия и соответствия API ключа в заголовках запроса.""" - api_key = request.headers.get("X-API-KEY") - return api_key == settings.API_DOCS_KEY diff --git a/adaptive_hockey_federation/video_api/serializers.py b/adaptive_hockey_federation/video_api/serializers.py deleted file mode 100644 index cf40a9ac..00000000 --- a/adaptive_hockey_federation/video_api/serializers.py +++ /dev/null @@ -1,119 +0,0 @@ -from django.conf import settings -from rest_framework import serializers - -from games.models import Game, GamePlayer -from main.models import Player - - -class GameFeatureSerializer(serializers.ModelSerializer): - """Сериализатор подготовки данных для отправки к DS.""" - - game_id = serializers.IntegerField( - source="id", - ) - game_link = serializers.CharField( - source="video_link", - read_only=True, - ) - team_ids = serializers.SerializerMethodField() - player_ids = serializers.SerializerMethodField() - player_numbers = serializers.SerializerMethodField() - token = serializers.SerializerMethodField() - - class Meta: - model = Game - fields = [ - "game_id", - "game_link", - "player_ids", - "player_numbers", - "team_ids", - "token", - ] - - def sort_player_by_team( - self, - obj: Game, - field_name: str, - ) -> list[list[int]]: - """ - Метод для сортировки игроков по командам. - - Сортировка в соответствии с порядком команд в поле team_ids. - """ - game_players = GamePlayer.objects.filter(game_team__game=obj) - team_players: list[tuple[int, int]] = list( - game_players.values_list("game_team_id", field_name), - ) - teams: dict[int, list[int]] = { - team_id: [] for team_id in self.get_team_ids(obj) - } - for team_id, field in team_players: - teams[team_id].append(field) - return list(teams.values()) - - def get_team_ids(self, obj): - """Метод получения ID команд.""" - return list(obj.game_teams.values_list("gameteam_id", flat=True)) - - def get_player_ids(self, obj): - """Метод получения ID игроков.""" - return self.sort_player_by_team(obj, "id") - - def get_player_numbers(self, obj): - """Метод получения номеров игроков.""" - return self.sort_player_by_team(obj, "number") - - def get_token(self, obj): - """Метод токена.""" - return settings.YANDEX_DISK_OAUTH_TOKEN - - -# TODO возможно верный сериализатор DS. Уточнить структуру ответа DS -class TrackingSerializer(serializers.Serializer): - """Сериализатор, обрабатывающий tracking с фреймами.""" - - player_id = serializers.PrimaryKeyRelatedField( - queryset=Player.objects.all(), - ) - team_id = serializers.IntegerField() - frames = serializers.ListField( - child=serializers.IntegerField(), - ) - boxes = serializers.ListField( - child=serializers.ListField( - child=serializers.IntegerField(), - ), - ) - time = serializers.ListField( - child=serializers.CharField(max_length=50), - ) - predicted_number = serializers.SerializerMethodField() - - def get_predicted_number(self, obj): - """Данные predicted_number могут быть как str так и int.""" - return obj.predicted_number - - -# TODO возможно верный сериализатор DS. Уточнить структуру ответа DS -class GameDataPlayerSerializer(serializers.Serializer): - """Сериалатор для маршалинга ответа от сервиса дс-ов.""" - - game_id = serializers.PrimaryKeyRelatedField( - queryset=Game.objects.all(), - ) - tracking = TrackingSerializer( - many=True, - ) - - -# TODO уточнить структуру ответа DS -class GameDataPlayerSerializerMock(serializers.Serializer): - """Сериализатор заглушка ответа DS.""" - - number = serializers.IntegerField() - team = serializers.IntegerField() - counter = serializers.IntegerField() - frames = serializers.ListField( - child=serializers.IntegerField(), - ) diff --git a/adaptive_hockey_federation/video_api/tasks.py b/adaptive_hockey_federation/video_api/tasks.py deleted file mode 100644 index 5cbe147f..00000000 --- a/adaptive_hockey_federation/video_api/tasks.py +++ /dev/null @@ -1,150 +0,0 @@ -# import json -import logging -import os - -# from django.db import transaction - -# from games.models import Game, GameDataPlayer, GamePlayer -# from main.models import Player -from service.a_hockey_requests import send_request_to_process_video -from service.video_processing import slicing_video_with_player_frames -# from users.utilits.send_mails import send_info_mail -# from .serializers import GameDataPlayerSerializerMock - - -logger = logging.getLogger(__name__) - - -def get_player_video_frames(game_data): - """Таск для запуска обработки видео.""" - logger.info("Добавлен новый объект игры, запускаем воркер") - return send_request_to_process_video(game_data) - - -def create_player_video( - *args, - **kwargs, -): - """Таск для нарезки видео с моментами игрока.""" - # Мок реализация фреймов для нарезки видео с моментами игрока. - # Пока подставляются тестовые фреймы. - # TODO удалить мок реализацию, как в бд появятся фреймы по игрокам. - - input_file = kwargs["input_file"] - output_file = kwargs["output_file"] - # player = kwargs["player"] - # game = kwargs["game"] - frames = kwargs["frames"] - if os.path.exists(output_file): - return - - slicing_video_with_player_frames(input_file, output_file, frames) - return f"Видео обработано. {args}" - -# TODO раскомментировать после добавления celery -# def bulk_create_gamedataplayer_objects(sender=None, **kwargs): -# """ -# Сохранение параметров видео игроков от сервера DS. -# Вызов таски нарезки видео. -# """ -# result = kwargs.get("result") -# task_params = sender.request.kwargs["data"] -# user_email = sender.request.kwargs["user_email"] - -# # TODO уточнить структуру ответа DS -# serializer = GameDataPlayerSerializerMock(data=result, many=True) -# if serializer.is_valid(): -# object_data = serializer.validated_data -# game = Game.objects.get(pk=task_params["game_id"]) -# with transaction.atomic(): -# for track in object_data: -# try: -# game_player = GamePlayer.objects.get( -# game_team__game=game, -# game_team_id=track["team"], -# number=track["number"], -# ) -# except GamePlayer.DoesNotExist: -# logger.warning( -# f"Игрок с номером {track['number']} " -# f"команды {track['team']} " -# f"в игре {task_params['game_id']} не найден.", -# ) -# continue -# except GamePlayer.MultipleObjectsReturned: -# logger.warning( -# f"В команде {track['team']} " -# f"несколько игроков с номером {track['number']} " -# f"участвовало в игре {task_params['game_id']}.", -# ) -# continue -# player = Player.objects.get(pk=game_player.id) -# # TODO возможно следует использовать bulk_create -# GameDataPlayer.objects.update_or_create( -# player=player, -# game=game, -# defaults={"data": json.dumps(track)}, -# ) -# logger.info( -# ( -# f"Cоздаем видео для игрока " -# f"{player.get_name_and_position()}" -# ), -# ) -# # TODO в args передают аргументы -# # нужные для нарезки видео с игроком -# # create_player_video( -# # TODO заменить название исходного файла -# # видео с игрой нужно скачать -# # ссылка на видео с игрой task_params["game_link"] -# input_file = "input_file.mp4" -# output_file = ( -# f"video_game_{task_params['game_id']}_" -# f"player_{game_player.id}.mp4" -# ) -# create_player_video.apply_async( -# args=["Обработка с низким приоритетом"], -# kwargs={ -# "input_file": input_file, -# "output_file": output_file, -# "player": str(player), -# "game": game.name, -# "user_email": user_email, -# "frames": track["frames"], -# "priority": 255, -# }, -# ) -# else: -# logger.error(serializer.errors) - -# TODO раскомментировать после добавления celery -# def on_pool_process_init(**kwargs): -# # Что бы отрабатывал сигнал task_success -# # https://github.com/celery/celery/issues/2343 -# # https://django.fun/docs/celery/5.1/userguide/signals/#worker-process-init -# task_success.connect( -# bulk_create_gamedataplayer_objects, -# sender=current_app.tasks[get_player_video_frames.name], -# ) - - -# TODO раскомментировать после добавления celery -# def send_success_mail(sender=None, **kwargs): -# """Вызывает функцию отправки письма о готовности видео с игроком.""" -# player = sender.request.kwargs["player"] -# game = sender.request.kwargs["game"] -# user_email = sender.request.kwargs["user_email"] -# send_info_mail( -# "Обработка видео завершена", -# f'Завершена обработка видео игрока {player} в игре "{game}".', -# user_email, -# ) - - -# TODO раскомментировать после добавления celery -# def mail_success_video_process(**kwargs): -# """Обработка сигнала task_success таски create_player_video.""" -# task_success.connect( -# send_success_mail, -# sender=current_app.tasks[create_player_video.name], -# ) From e7f68c8697e2abe9b46aea3393a45b06c381f4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 17:27:30 +0500 Subject: [PATCH 059/126] test deploy --- .github/workflows/prod_deploy.yaml | 201 ++++++++++++---------------- infra/nginx/nginx_prod.conf | 34 ----- infra/prod/docker-compose.prod.yaml | 38 ++++-- 3 files changed, 115 insertions(+), 158 deletions(-) delete mode 100644 infra/nginx/nginx_prod.conf diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 7b9643b7..2f8eb2bc 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,125 +15,98 @@ defaults: working-directory: . jobs: -# code_style_pep8: -# runs-on: ubuntu-latest -# name: ruff -# steps: -# - name: Install Python -# uses: actions/setup-python@v4 -# with: -# python-version: 3.11 - -# - name: Install Poetry -# uses: snok/install-poetry@v1 -# with: -# poetry-version: 1.5.0 - -# - name: Check out the repo -# uses: actions/checkout@v4 - -# - name: Установка зависимостей -# run: | -# poetry install - -# - name: ruff -# run: | -# poetry run ruff check -# pytest: -# needs: code_style_pep8 -# runs-on: ubuntu-latest -# name: pytest -# steps: -# - name: Install Python -# uses: actions/setup-python@v4 -# with: -# python-version: 3.11 - -# - name: Install Poetry -# uses: snok/install-poetry@v1 -# with: -# poetry-version: 1.5.0 - -# - name: Check out the repo -# uses: actions/checkout@v4 - -# - name: Install dependencies -# run: | -# poetry install -# - name: pytest -# run: | -# poetry run pytest -# working-directory: adaptive_hockey_federation - # build_and_push: - # # needs: [code_style_pep8, pytest] - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ${{ env.REGISTRY }} - # username: ${{ github.actor }} - # password: ${{ secrets.GITHUB_TOKEN }} - - # - name: Extract metadata for Docker - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # - name: Build and push Docker image for Production - # uses: docker/build-push-action@v5 - # with: - # context: . - # file: infra/prod/prod.Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} - - deploy: - name: Deploy changes on server - runs-on: ubuntu-latest - environment: - name: prod_deploy - # needs: pytest - steps: - - name: Checkout repository + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo uses: actions/checkout@v4 - - name: delete stage & dev + - name: Install dependencies + run: | + poetry install + - name: pytest run: | - rm -r infra/stage - rm -r infra/dev + poetry run pytest + working-directory: adaptive_hockey_federation + build_and_push: + needs: pytest + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Copy prod via ssh - uses: appleboy/scp-action@master + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra" - target: "${{ env.DEPLOY_PATH }}" - overwrite: true - - # - name: Copy dev folder to VPS - # run: | - # scp -r $GITHUB_WORKSPACE/infra/prod/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - # scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ env.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - - # - name: Execute commands on VPS - # uses: appleboy/ssh-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ env.USERNAME }} - # key: ${{ secrets.TEST_RSA_SECRET_KEY }} - # script: | - # cd ${{ env.DEPLOY_PATH }} - # rm .env - # touch .env + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + deploy: + name: Deploy changes on server + runs-on: ubuntu-latest + needs: [pytest, build_and_push] + environment: + name: prod_deploy + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Delete stage & dev + run: | + rm -r infra/stage + rm -r infra/dev + + - name: Copy prod via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra" + target: "${{ env.DEPLOY_PATH }}" + overwrite: true + + - name: Execute commands on VPS + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ env.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + script: | + cd ${{ env.DEPLOY_PATH }} + rm .env + touch .env + + echo "${{ secrets.ENV_FILE }}" > .env # echo HOST=${{ secrets.HOST }} >> .env # echo PORT=${{ secrets.PORT }} >> .env diff --git a/infra/nginx/nginx_prod.conf b/infra/nginx/nginx_prod.conf deleted file mode 100644 index f5dabf4d..00000000 --- a/infra/nginx/nginx_prod.conf +++ /dev/null @@ -1,34 +0,0 @@ -server{ - listen 80; - listen [::]:80; - server_name _; - return 308 https://$host$request_uri; -} - -server{ - listen 443 ssl http2; - listen [::]:443 ssl http2; - server_name ${HOST}; - include /config/nginx/ssl.conf; - location / { - proxy_pass http://site:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - location /admin/ { - proxy_pass http://site:8000/admin/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - location /media/ { - root /var/html/; - } - - location /static/ { - root /var/html/; - } -} \ No newline at end of file diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 64236b3c..dc83a6b8 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -1,11 +1,13 @@ version: '3.8' -name: ahf_prod +name: ahf_stage services: db: container_name: db image: postgres:13.0-alpine restart: always + ports: + - 5432:${DB_PORT} volumes: - postgres_data:/var/lib/postgresql/data/ env_file: @@ -13,27 +15,43 @@ services: site: image: "${IMAGE_COMPOSE}" + container_name: adaptive_hockey_federation restart: always volumes: - - static_value:/app/static/ - - media_value:/app/media/ + - static_value:/app/adaptive_hockey_federation/static/ + - ../../media:/app/adaptive_hockey_federation/media/ env_file: - ../../.env depends_on: - db - nginx: - image: nginx:1.21.3-alpine - ports: - - "80:80" + swag: + image: lscr.io/linuxserver/swag:latest + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID=1002 + - PGID=1004 + - TZ=Europe/Moscow + - URL=${HOST} + - VALIDATION=http + - STAGING=${ST} volumes: - - ../nginx/nginx_prod.conf:/etc/nginx/conf.d/default.conf + - ../nginx/nginx_stage.conf:/config/nginx/site-confs/default.conf + - swag_volume_stage:/config - static_value:/var/html/static/ - - media_value:/var/html/media/ + - ../../media:/var/html/media/ + ports: + - 443:443 + - 80:${PORT} + env_file: + - ../../.env depends_on: - site + restart: unless-stopped volumes: static_value: - media_value: postgres_data: + swag_volume_stage: From 695d6bd231d202b6bc7ba0100e4af10143c6c2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 17:41:21 +0500 Subject: [PATCH 060/126] test deploy1 --- .github/workflows/prod_deploy.yaml | 126 ++++++++++++++--------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 2f8eb2bc..58e088ef 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,63 +15,63 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - build_and_push: - needs: pytest - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + # pytest: + # runs-on: ubuntu-latest + # name: pytest + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation + # build_and_push: + # needs: pytest + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # - name: Build and push Docker image for Production + # uses: docker/build-push-action@v5 + # with: + # context: . + # file: infra/prod/prod.Dockerfile + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - needs: [pytest, build_and_push] + # needs: [pytest, build_and_push] environment: name: prod_deploy steps: @@ -83,16 +83,16 @@ jobs: rm -r infra/stage rm -r infra/dev - - name: Copy prod via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra" - target: "${{ env.DEPLOY_PATH }}" - overwrite: true + # - name: Copy prod via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra" + # target: "${{ env.DEPLOY_PATH }}" + # overwrite: true - name: Execute commands on VPS uses: appleboy/ssh-action@master From 807c7aaf6e93e20b1b28336eaa0bf0587bf7fa8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 17:43:04 +0500 Subject: [PATCH 061/126] test deploy2 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 58e088ef..1e6fb021 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -98,7 +98,7 @@ jobs: uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} - username: ${{ env.USERNAME }} + username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} script: | From aa4065fe860bab16799a48d8ce7bbc216c8763e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 18:03:40 +0500 Subject: [PATCH 062/126] test deploy3 --- .github/workflows/prod_deploy.yaml | 64 ++++++++++-------------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 1e6fb021..6b0d9351 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -108,47 +108,23 @@ jobs: echo "${{ secrets.ENV_FILE }}" > .env - # echo HOST=${{ secrets.HOST }} >> .env - # echo PORT=${{ secrets.PORT }} >> .env - # echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env - # echo ST=${{ secrets.ST }} >> .env - - # echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env - # echo DEBUG=${{ secrets.DEBUG }} >> .env - # echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env - # echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env - - # echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env - # echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env - # echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env - # echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env - # echo DB_HOST=${{ secrets.DB_HOST }} >> .env - # echo DB_PORT=${{ secrets.DB_PORT }} >> .env - - # echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env - # echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env - # echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env - # echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env - # echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env - # echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env - - # cd infra/prod/ - # sudo systemctl stop adaptive_hockey_federation.service - # docker system prune --force - - # # Installing defend service for app - # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - # sudo systemctl daemon-reload - # sudo systemctl start adaptive_hockey_federation.service - - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # until [ $? -eq 0 ]; do - # echo "Waiting for adaptive_hockey_federation.service to be active..." - # sleep 5 - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # done - - # echo "adaptive_hockey_federation.service is active" - - # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - # docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + cd infra/prod/ + sudo systemctl stop adaptive_hockey_federation.service + docker system prune --force + + # Installing defend service for app + sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + sudo systemctl daemon-reload + sudo systemctl start adaptive_hockey_federation.service + + sudo systemctl is-active --quiet adaptive_hockey_federation.service + until [ $? -eq 0 ]; do + echo "Waiting for adaptive_hockey_federation.service to be active..." + sleep 5 + sudo systemctl is-active --quiet adaptive_hockey_federation.service + done + + echo "adaptive_hockey_federation.service is active" + + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file From 891c452ad3df52f2f708c8cc027093c8c033a3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 18:29:38 +0500 Subject: [PATCH 063/126] test deploy4 --- .github/workflows/prod_deploy.yaml | 126 +++++++++--------- infra/prod/adaptive_hockey_federation.service | 4 +- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 6b0d9351..9fe87412 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,63 +15,63 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation - # build_and_push: - # needs: pytest - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ${{ env.REGISTRY }} - # username: ${{ github.actor }} - # password: ${{ secrets.GITHUB_TOKEN }} - - # - name: Extract metadata for Docker - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # - name: Build and push Docker image for Production - # uses: docker/build-push-action@v5 - # with: - # context: . - # file: infra/prod/prod.Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + build_and_push: + needs: pytest + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - # needs: [pytest, build_and_push] + needs: [pytest, build_and_push] environment: name: prod_deploy steps: @@ -83,16 +83,16 @@ jobs: rm -r infra/stage rm -r infra/dev - # - name: Copy prod via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra" - # target: "${{ env.DEPLOY_PATH }}" - # overwrite: true + - name: Copy prod via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra" + target: "${{ env.DEPLOY_PATH }}" + overwrite: true - name: Execute commands on VPS uses: appleboy/ssh-action@master diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index 0efc8685..fb80c55b 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -23,8 +23,8 @@ ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down # Call when daemon already start -ExecStartPost=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env ps - +# ExecStartPost=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env ps +ExecStartPost=docker container ps [Install] From c786a8e0c5671affbe705cc3022790aa07bd18cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 18:49:55 +0500 Subject: [PATCH 064/126] test deploy5 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 9fe87412..61908e18 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,7 +103,7 @@ jobs: passphrase: ${{ secrets.SSH_PASSPHRASE }} script: | cd ${{ env.DEPLOY_PATH }} - rm .env + # rm .env touch .env echo "${{ secrets.ENV_FILE }}" > .env From 846ba06e979633825529b06e7d4ca58965546562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 19:06:16 +0500 Subject: [PATCH 065/126] test deploy6 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 61908e18..9fe87412 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -103,7 +103,7 @@ jobs: passphrase: ${{ secrets.SSH_PASSPHRASE }} script: | cd ${{ env.DEPLOY_PATH }} - # rm .env + rm .env touch .env echo "${{ secrets.ENV_FILE }}" > .env From 511055236be4a85e3985bf3f031ef2a3592e1b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 19:30:25 +0500 Subject: [PATCH 066/126] test deploy7 --- infra/prod/adaptive_hockey_federation.service | 1 - 1 file changed, 1 deletion(-) diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index fb80c55b..19208f60 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -23,7 +23,6 @@ ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down # Call when daemon already start -# ExecStartPost=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env ps ExecStartPost=docker container ps [Install] From fedd3f28c3df028345c57d8b12b16256997be075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 19:41:12 +0500 Subject: [PATCH 067/126] test deploy8 --- .github/workflows/prod_deploy.yaml | 3 +-- infra/nginx/nginx_prod.conf | 39 +++++++++++++++++++++++++++++ infra/prod/docker-compose.prod.yaml | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 infra/nginx/nginx_prod.conf diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 9fe87412..c408d675 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -93,7 +93,7 @@ jobs: source: "infra" target: "${{ env.DEPLOY_PATH }}" overwrite: true - + - name: Execute commands on VPS uses: appleboy/ssh-action@master with: @@ -103,7 +103,6 @@ jobs: passphrase: ${{ secrets.SSH_PASSPHRASE }} script: | cd ${{ env.DEPLOY_PATH }} - rm .env touch .env echo "${{ secrets.ENV_FILE }}" > .env diff --git a/infra/nginx/nginx_prod.conf b/infra/nginx/nginx_prod.conf new file mode 100644 index 00000000..73bcb779 --- /dev/null +++ b/infra/nginx/nginx_prod.conf @@ -0,0 +1,39 @@ +server { + listen 80; + listen [::]:80; + server_name _; + return 308 https://$host$request_uri; +} + +server { + listen 443 ssl http2; # REMOVED default_server + listen [::]:443 ssl http2; # REMOVED default_server + + server_name ${HOST}; # PUT YOUR DOMAIN HERE + + include /config/nginx/ssl.conf; + + location / { + proxy_pass http://site:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /admin/ { + proxy_pass http://site:8000/admin/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /media/ { + root /var/html/; + } + + location /static/ { + root /var/html/; + } +} diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index dc83a6b8..685219c4 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -38,7 +38,7 @@ services: - VALIDATION=http - STAGING=${ST} volumes: - - ../nginx/nginx_stage.conf:/config/nginx/site-confs/default.conf + - ../nginx/nginx_prod.conf:/config/nginx/site-confs/default.conf - swag_volume_stage:/config - static_value:/var/html/static/ - ../../media:/var/html/media/ From 73922231c5f0283a9643519d3519a4aa429b41ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 19:52:22 +0500 Subject: [PATCH 068/126] test deploy8 --- .github/workflows/prod_deploy.yaml | 110 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index c408d675..1f5f8c88 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,63 +15,63 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - build_and_push: - needs: pytest - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + # pytest: + # runs-on: ubuntu-latest + # name: pytest + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation + # build_and_push: + # needs: pytest + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # - name: Build and push Docker image for Production + # uses: docker/build-push-action@v5 + # with: + # context: . + # file: infra/prod/prod.Dockerfile + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - needs: [pytest, build_and_push] + # needs: [pytest, build_and_push] environment: name: prod_deploy steps: @@ -125,5 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + sudo docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + sudo docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file From d8bc2efef2a575bcbec4fe0a2b7808413cc82d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:00:15 +0500 Subject: [PATCH 069/126] test deploy9 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 1f5f8c88..8d9bd3a5 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -125,5 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - sudo docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - sudo docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file From 0f3e7a12232eda1637da05ed528271ca85d48c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:15:17 +0500 Subject: [PATCH 070/126] test deploy10 --- .github/workflows/prod_deploy.yaml | 4 ++-- infra/prod/adaptive_hockey_federation.service | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 8d9bd3a5..eb6cc863 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -125,5 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + docker compose exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker compose exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index 19208f60..c78b07e5 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -13,8 +13,10 @@ User=production WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env down +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull site +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull db +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull swag # compose up ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env up From 5e421b8f0addd740475668aee53d175c35ca5570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:23:33 +0500 Subject: [PATCH 071/126] test deploy11 --- infra/prod/adaptive_hockey_federation.service | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index c78b07e5..56f0f53a 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -13,10 +13,10 @@ User=production WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ -ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env down -ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull site -ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull db -ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull swag +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull site +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull db +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull swag # compose up ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env up @@ -25,7 +25,7 @@ ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down # Call when daemon already start -ExecStartPost=docker container ps +ExecStartPost=echo "Service started" [Install] From 36c394c03a9d55e7f5ab30ccaa829b85c2ab57d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:36:34 +0500 Subject: [PATCH 072/126] test deploy12 --- infra/prod/adaptive_hockey_federation.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index 56f0f53a..f31429b5 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -25,7 +25,7 @@ ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down # Call when daemon already start -ExecStartPost=echo "Service started" +ExecStartPost= [Install] From aab2f020f86369905f7d3cad63d81e438efe61bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:39:26 +0500 Subject: [PATCH 073/126] test deploy13 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index eb6cc863..fb5ebff4 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -125,5 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - docker compose exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker compose exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate From da3678115b82e832934cdeb93e68eb07ae3d0d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:43:34 +0500 Subject: [PATCH 074/126] test deploy14 --- .github/workflows/prod_deploy.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index fb5ebff4..99a7d862 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -125,5 +125,7 @@ jobs: echo "adaptive_hockey_federation.service is active" - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate + # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + # docker exec adaptive_hockey_federation python manage.py migrate + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec python manage.py collectstatic --noinput + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec python manage.py migrate \ No newline at end of file From 565f906611c7a93329ad71f7318791498cbb0ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:46:26 +0500 Subject: [PATCH 075/126] test deploy15 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 99a7d862..b005bb12 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -127,5 +127,5 @@ jobs: # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput # docker exec adaptive_hockey_federation python manage.py migrate - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec python manage.py collectstatic --noinput - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec python manage.py migrate \ No newline at end of file + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec site python manage.py collectstatic --noinput + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec site python manage.py migrate \ No newline at end of file From 48ec311a02dfe9673211020fe1eeddfe54a06212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:48:22 +0500 Subject: [PATCH 076/126] test deploy16 --- .github/workflows/prod_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index b005bb12..2d248239 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -127,5 +127,5 @@ jobs: # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput # docker exec adaptive_hockey_federation python manage.py migrate - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec site python manage.py collectstatic --noinput - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec site python manage.py migrate \ No newline at end of file + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file From 4ddddd5507664108c9fe5eda2a16721ae8ec1998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:54:19 +0500 Subject: [PATCH 077/126] test deploy17 --- infra/prod/adaptive_hockey_federation.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index f31429b5..ab7b15c8 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -25,7 +25,7 @@ ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down # Call when daemon already start -ExecStartPost= +ExecStartPost=sleep 5 [Install] From 1ed0ca7b4c1a9c5584f2f7e7f334b31988a1f013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 20:58:55 +0500 Subject: [PATCH 078/126] test deploy18 --- .github/workflows/prod_deploy.yaml | 6 ++---- infra/prod/adaptive_hockey_federation.service | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 2d248239..fb5ebff4 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -125,7 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - # docker exec adaptive_hockey_federation python manage.py migrate - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env exec adaptive_hockey_federation python manage.py migrate \ No newline at end of file + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index ab7b15c8..25bfa9bf 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -14,7 +14,7 @@ User=production WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull site +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull adaptive_hockey_federation ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull db ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull swag From 786827b65c207a4d155a4ec6ea5b1c91292f9320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:09:18 +0500 Subject: [PATCH 079/126] test deploy19 --- infra/prod/adaptive_hockey_federation.service | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service index 25bfa9bf..3c5193ff 100644 --- a/infra/prod/adaptive_hockey_federation.service +++ b/infra/prod/adaptive_hockey_federation.service @@ -13,10 +13,9 @@ User=production WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull adaptive_hockey_federation -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull db -ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull swag + # compose up ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env up From 8c6827c3c5b796091a93432973fbedcfbdf04fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:26:27 +0500 Subject: [PATCH 080/126] test deploy20 --- infra/nginx/{nginx_prod.conf => default.conf} | 0 infra/prod/docker-compose.prod.yaml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename infra/nginx/{nginx_prod.conf => default.conf} (100%) diff --git a/infra/nginx/nginx_prod.conf b/infra/nginx/default.conf similarity index 100% rename from infra/nginx/nginx_prod.conf rename to infra/nginx/default.conf diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 685219c4..df7f5f65 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -38,7 +38,7 @@ services: - VALIDATION=http - STAGING=${ST} volumes: - - ../nginx/nginx_prod.conf:/config/nginx/site-confs/default.conf + - ../nginx/default.conf:/config/nginx/site-confs/default.conf - swag_volume_stage:/config - static_value:/var/html/static/ - ../../media:/var/html/media/ From ecba4ae70d46e7c250d66ad350be8d2c7b55dbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:30:18 +0500 Subject: [PATCH 081/126] test deploy21 --- infra/nginx/nginx_stage.conf | 39 ------------------------------------ 1 file changed, 39 deletions(-) delete mode 100644 infra/nginx/nginx_stage.conf diff --git a/infra/nginx/nginx_stage.conf b/infra/nginx/nginx_stage.conf deleted file mode 100644 index 73bcb779..00000000 --- a/infra/nginx/nginx_stage.conf +++ /dev/null @@ -1,39 +0,0 @@ -server { - listen 80; - listen [::]:80; - server_name _; - return 308 https://$host$request_uri; -} - -server { - listen 443 ssl http2; # REMOVED default_server - listen [::]:443 ssl http2; # REMOVED default_server - - server_name ${HOST}; # PUT YOUR DOMAIN HERE - - include /config/nginx/ssl.conf; - - location / { - proxy_pass http://site:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /admin/ { - proxy_pass http://site:8000/admin/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /media/ { - root /var/html/; - } - - location /static/ { - root /var/html/; - } -} From 28fce2ad51dc9d0d4abeff5a23755bf7a180b760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:34:02 +0500 Subject: [PATCH 082/126] pre-realease --- .github/workflows/prod_deploy.yaml | 104 ++++++++++++++--------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index fb5ebff4..09829dc9 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,58 +15,58 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation - # build_and_push: - # needs: pytest - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ${{ env.REGISTRY }} - # username: ${{ github.actor }} - # password: ${{ secrets.GITHUB_TOKEN }} - - # - name: Extract metadata for Docker - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # - name: Build and push Docker image for Production - # uses: docker/build-push-action@v5 - # with: - # context: . - # file: infra/prod/prod.Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + build_and_push: + needs: pytest + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server From 456a6eb73593180d56373fd9c1b9d583b8e7628d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:46:26 +0500 Subject: [PATCH 083/126] pre-realease1 --- .github/workflows/prod_deploy.yaml | 104 ++++++++++++++--------------- infra/nginx/default.conf | 6 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 09829dc9..fb5ebff4 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -15,58 +15,58 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - build_and_push: - needs: pytest - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + # pytest: + # runs-on: ubuntu-latest + # name: pytest + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation + # build_and_push: + # needs: pytest + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # - name: Build and push Docker image for Production + # uses: docker/build-push-action@v5 + # with: + # context: . + # file: infra/prod/prod.Dockerfile + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server diff --git a/infra/nginx/default.conf b/infra/nginx/default.conf index 73bcb779..bb600def 100644 --- a/infra/nginx/default.conf +++ b/infra/nginx/default.conf @@ -6,10 +6,10 @@ server { } server { - listen 443 ssl http2; # REMOVED default_server - listen [::]:443 ssl http2; # REMOVED default_server + listen 443 ssl http2; + listen [::]:443 ssl http2; - server_name ${HOST}; # PUT YOUR DOMAIN HERE + server_name ${HOST}; include /config/nginx/ssl.conf; From a0b3362568ac0c1e2ba5a22fea99003eabe2c858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Fri, 27 Sep 2024 21:51:12 +0500 Subject: [PATCH 084/126] pre-realease2 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index fb5ebff4..b6ec6a0f 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -92,7 +92,7 @@ jobs: passphrase: ${{ secrets.SSH_PASSPHRASE }} source: "infra" target: "${{ env.DEPLOY_PATH }}" - overwrite: true + rm: true - name: Execute commands on VPS uses: appleboy/ssh-action@master From 1bc1efc323a07ea24c6366d288bc902248ab2fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 10:12:57 +0500 Subject: [PATCH 085/126] pre-realease3 --- .github/workflows/build-and-push-github-packages.yaml | 3 ++- .github/workflows/prod_deploy.yaml | 7 ++++--- .github/workflows/pytest.yaml | 6 +++++- requirements/production.txt | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index d1d7d827..6569f4f3 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -5,6 +5,7 @@ on: branches: - master - dev + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io @@ -31,7 +32,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/feature/convert_ci-cd_to_the_prod' uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index b6ec6a0f..14f373a0 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,9 +1,10 @@ name: Production deploy on: - push: - branches: - - feature/convert_ci-cd_to_the_prod + workflow_run: + workflows: [pytest, Build and push Docker image] + types: completed + branches: feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index bd9d9bc5..e8d4be59 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -1,6 +1,10 @@ name: CI -on: [pull_request] +# on: [pull_request] +on: + push: + branches: + - feature/convert_ci-cd_to_the_prod jobs: pytest: diff --git a/requirements/production.txt b/requirements/production.txt index 1f8987ff..ffc2e93d 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -12,7 +12,6 @@ click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" -django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" django==4.2.13 ; python_version >= "3.11" and python_version < "4.0" djangorestframework==3.15.2 ; python_version >= "3.11" and python_version < "4.0" From 94057208b96cd0ffe1f5f11c37b114e46096509f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 10:15:03 +0500 Subject: [PATCH 086/126] pre-realease4 --- .../build-and-push-github-packages.yaml | 3 +- .github/workflows/prod_deploy.yaml | 113 +++++++++--------- .github/workflows/pytest.yaml | 6 +- 3 files changed, 58 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index 6569f4f3..309604e9 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -5,7 +5,6 @@ on: branches: - master - dev - - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io @@ -32,7 +31,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/feature/convert_ci-cd_to_the_prod' + if: github.ref == 'refs/heads/feature/master' uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 14f373a0..8e15dcc8 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,10 +1,9 @@ name: Production deploy on: - workflow_run: - workflows: [pytest, Build and push Docker image] - types: completed - branches: feature/convert_ci-cd_to_the_prod + push: + branches: + - master env: REGISTRY: ghcr.io @@ -16,63 +15,63 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation - # build_and_push: - # needs: pytest - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ${{ env.REGISTRY }} - # username: ${{ github.actor }} - # password: ${{ secrets.GITHUB_TOKEN }} - - # - name: Extract metadata for Docker - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # - name: Build and push Docker image for Production - # uses: docker/build-push-action@v5 - # with: - # context: . - # file: infra/prod/prod.Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + build_and_push: + needs: pytest + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - # needs: [pytest, build_and_push] + needs: [pytest, build_and_push] environment: name: prod_deploy steps: diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index e8d4be59..bd9d9bc5 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -1,10 +1,6 @@ name: CI -# on: [pull_request] -on: - push: - branches: - - feature/convert_ci-cd_to_the_prod +on: [pull_request] jobs: pytest: From 83a9f17414ecf37f7284fec95b7a1148f9d8a577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 10:16:41 +0500 Subject: [PATCH 087/126] pre-realease5 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 8e15dcc8..44b4f63e 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - master + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io From fa2d1db089d04691a1e3d2df5938beee70dda046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 10:52:52 +0500 Subject: [PATCH 088/126] pre-realease6 --- .github/workflows/prod_deploy.yaml | 1 - .../core/config/dev_settings.py | 2 +- .../core/config/prod_settings.py | 78 +++++++++++++++++++ adaptive_hockey_federation/manage.py | 2 +- .../nginx/{default.conf => nginx_stage.conf} | 0 infra/prod/docker-compose.prod.yaml | 2 +- 6 files changed, 81 insertions(+), 4 deletions(-) rename infra/nginx/{default.conf => nginx_stage.conf} (100%) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 44b4f63e..1d14b9da 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -4,7 +4,6 @@ on: push: branches: - feature/convert_ci-cd_to_the_prod - env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} diff --git a/adaptive_hockey_federation/core/config/dev_settings.py b/adaptive_hockey_federation/core/config/dev_settings.py index 89eae34e..446035cb 100644 --- a/adaptive_hockey_federation/core/config/dev_settings.py +++ b/adaptive_hockey_federation/core/config/dev_settings.py @@ -1,4 +1,4 @@ -from .base_settings import * +from . base_settings import * ROOT_DIR = BASE_DIR.parent diff --git a/adaptive_hockey_federation/core/config/prod_settings.py b/adaptive_hockey_federation/core/config/prod_settings.py index e69de29b..5d8cf3c1 100644 --- a/adaptive_hockey_federation/core/config/prod_settings.py +++ b/adaptive_hockey_federation/core/config/prod_settings.py @@ -0,0 +1,78 @@ +from .base_settings import * + +ROOT_DIR = BASE_DIR.parent + +env.read_env(ROOT_DIR / ".env") + +INTERNAL_IPS = [ + "127.0.0.1", +] + +DATABASES = { + "default": { + "ENGINE": env("DB_ENGINE", default="django.db.backends.postgresql"), + "NAME": env("POSTGRES_DB", default="postgres_db"), + "USER": env("POSTGRES_USER", default="postgres_user"), + "PASSWORD": env("POSTGRES_PASSWORD", default="postgres_password"), + "HOST": env("DB_HOST", default="localhost"), + "PORT": env("DB_PORT", default="5432"), + } +} + +FIXSTURES_DIR = BASE_DIR / "core" / "fixtures" +JSON_PARSER_FILE = "data.json" +FIXSTURES_FILE = FIXSTURES_DIR / JSON_PARSER_FILE +RESOURSES_ROOT = BASE_DIR / "resourses" + +# Важен порядок ключей для вставки/удаления +FILE_MODEL_MAP = { + "main_player_team": "Player", + "main_player": "Player", + "main_team": "Team", + "main_staffteammember": "StaffTeamMember", + "main_staffmember": "StaffMember", + "main_city": "City", + "main_diagnosis": "Diagnosis", + "main_nosology": "Nosology", + "main_disciplinelevel": "DisciplineLevel", + "main_disciplinename": "DisciplineName", +} + +EMAIL_BACKEND = env.str( + "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" +) + +EMAIL_TEMPLATE_NAME = "emailing/email.html" + +EMAIL_HOST = env.str("EMAIL_HOST", default="smtp.yandex.ru") + +try: + EMAIL_PORT = env.int("EMAIL_PORT", default=587) +except ValueError: + EMAIL_PORT = 587 + +EMAIL_HOST_USER = env.str("EMAIL_HOST_USER", default="example@yandex.ru") + +EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD", default="password") + +EMAIL_USE_TLS = env.str("EMAIL_USE_TLS", default=True) + +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER + +SERVER_EMAIL = EMAIL_HOST_USER + +EMAIL_ADMIN = EMAIL_HOST_USER + +ADMIN_PAGE_ORDERING = { + "main": [ + "Player", + "Team", + "StaffMember", + "DisciplineName", + "DisciplineLevel", + "City", + "Nosology", + "Diagnosis", + "GameDataPlayer", + ], +} diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index e64a9f79..ac32aa37 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -7,7 +7,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.config.dev_settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.config.prod_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/infra/nginx/default.conf b/infra/nginx/nginx_stage.conf similarity index 100% rename from infra/nginx/default.conf rename to infra/nginx/nginx_stage.conf diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index df7f5f65..dc83a6b8 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -38,7 +38,7 @@ services: - VALIDATION=http - STAGING=${ST} volumes: - - ../nginx/default.conf:/config/nginx/site-confs/default.conf + - ../nginx/nginx_stage.conf:/config/nginx/site-confs/default.conf - swag_volume_stage:/config - static_value:/var/html/static/ - ../../media:/var/html/media/ From c3f8f184783b688c62caf4f1d94e72641b15a5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 13:32:17 +0500 Subject: [PATCH 089/126] pre-realease7 --- .github/workflows/prod_deploy.yaml | 8 ++++---- infra/prod/docker-compose.prod.yaml | 3 ++- infra/prod/prod.Dockerfile | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 1d14b9da..419c2138 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - feature/convert_ci-cd_to_the_prod + - dev env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} @@ -82,7 +82,7 @@ jobs: rm -r infra/stage rm -r infra/dev - - name: Copy prod via ssh + - name: Copy infra via ssh uses: appleboy/scp-action@master with: host: ${{ secrets.HOST }} @@ -124,5 +124,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate + # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + # docker exec adaptive_hockey_federation python manage.py migrate diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index dc83a6b8..c462d676 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -1,5 +1,5 @@ version: '3.8' -name: ahf_stage +name: ahf_prod services: db: @@ -22,6 +22,7 @@ services: - ../../media:/app/adaptive_hockey_federation/media/ env_file: - ../../.env + command: bash -c "python manage.py migrate && python manage.py collectstatic --noinput && gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 4" depends_on: - db diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index 8d5d4cf3..b8a4347d 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -10,4 +10,4 @@ COPY . . WORKDIR /app/adaptive_hockey_federation -CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] +# CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] From df1e6684843df6d723c3741178b2e3efba4f5279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 13:36:42 +0500 Subject: [PATCH 090/126] pre-realease8 --- .github/workflows/prod_deploy.yaml | 2 +- adaptive_hockey_federation/manage.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 419c2138..1b6d252c 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - dev + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index ac32aa37..d730851a 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -7,7 +7,8 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.config.prod_settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "core.config.prod_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: From d8cc99af5d3cd3baa7e8727a728388e802be7c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 13:46:09 +0500 Subject: [PATCH 091/126] pre-realease9 --- infra/prod/prod.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index b8a4347d..28520c05 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -9,5 +9,4 @@ COPY . . WORKDIR /app/adaptive_hockey_federation - # CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] From 875a0ddca639eb63c6ab7cb1d29f43fb27258532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 13:58:52 +0500 Subject: [PATCH 092/126] pre-realease10 --- .github/workflows/prod_deploy.yaml | 115 ++++++++++++++-------------- infra/prod/docker-compose.prod.yaml | 1 - infra/prod/prod.Dockerfile | 2 +- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 1b6d252c..12974f93 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -14,63 +14,63 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation - build_and_push: - needs: pytest - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + # pytest: + # runs-on: ubuntu-latest + # name: pytest + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation + # build_and_push: + # needs: pytest + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v3 + + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.actor }} + # password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # - name: Build and push Docker image for Production + # uses: docker/build-push-action@v5 + # with: + # context: . + # file: infra/prod/prod.Dockerfile + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - needs: [pytest, build_and_push] + # needs: [pytest, build_and_push] environment: name: prod_deploy steps: @@ -89,9 +89,10 @@ jobs: username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra" - target: "${{ env.DEPLOY_PATH }}" + source: "infra/" + target: "${{ env.DEPLOY_PATH }}/infra" rm: true + strip_components: 1 - name: Execute commands on VPS uses: appleboy/ssh-action@master @@ -124,5 +125,5 @@ jobs: echo "adaptive_hockey_federation.service is active" - # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - # docker exec adaptive_hockey_federation python manage.py migrate + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index c462d676..9e978a92 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -22,7 +22,6 @@ services: - ../../media:/app/adaptive_hockey_federation/media/ env_file: - ../../.env - command: bash -c "python manage.py migrate && python manage.py collectstatic --noinput && gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 4" depends_on: - db diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index 28520c05..749cfb42 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -9,4 +9,4 @@ COPY . . WORKDIR /app/adaptive_hockey_federation -# CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] +CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] From d2abf66a259155e14088b7afce6f527e86e65211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 14:10:54 +0500 Subject: [PATCH 093/126] pre-realease11 --- infra/prod/docker-compose.prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 9e978a92..dc83a6b8 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -1,5 +1,5 @@ version: '3.8' -name: ahf_prod +name: ahf_stage services: db: From 4e6eb8bcb0705afb5c913bab4cfc032685ff0e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 14:18:09 +0500 Subject: [PATCH 094/126] on review --- .github/workflows/prod_deploy.yaml | 110 +++++++++++++++-------------- requirements/production.txt | 3 - 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 12974f93..b8c657f3 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - feature/convert_ci-cd_to_the_prod + - dev env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} @@ -14,63 +14,65 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation - # build_and_push: - # needs: pytest - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ${{ env.REGISTRY }} - # username: ${{ github.actor }} - # password: ${{ secrets.GITHUB_TOKEN }} - - # - name: Extract metadata for Docker - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # - name: Build and push Docker image for Production - # uses: docker/build-push-action@v5 - # with: - # context: . - # file: infra/prod/prod.Dockerfile - # push: true - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} + pytest: + runs-on: ubuntu-latest + name: pytest + + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + + build_and_push: + needs: pytest + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server runs-on: ubuntu-latest - # needs: [pytest, build_and_push] + needs: [pytest, build_and_push] environment: name: prod_deploy steps: diff --git a/requirements/production.txt b/requirements/production.txt index ffc2e93d..b28b80a2 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -3,9 +3,6 @@ asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" async-timeout==4.0.3 ; python_version >= "3.11" and python_full_version < "3.11.3" attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" billiard==4.2.0 ; python_version >= "3.11" and python_version < "4.0" -celery-singleton==0.3.1 ; python_version >= "3.11" and python_version < "4.0" -celery==5.4.0 ; python_version >= "3.11" and python_version < "4.0" -celery[redis]==5.4.0 ; python_version >= "3.11" and python_version < "4.0" click-didyoumean==0.3.1 ; python_version >= "3.11" and python_version < "4.0" click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" From c87c5da6b081f4b7717e3b6790e3ced2afcb0f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 18:29:00 +0500 Subject: [PATCH 095/126] test build --- .github/workflows/prod_deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index b8c657f3..df809da6 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -113,7 +113,6 @@ jobs: sudo systemctl stop adaptive_hockey_federation.service docker system prune --force - # Installing defend service for app sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service sudo systemctl daemon-reload sudo systemctl start adaptive_hockey_federation.service From cb568becd186cb375c5d8ff82eb7a993aa856101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 18:32:02 +0500 Subject: [PATCH 096/126] test build1 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index df809da6..d4651f82 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - dev + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} From c04aff328c2a72848189abf3bf91984ce00ab405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 18:41:31 +0500 Subject: [PATCH 097/126] test build2 --- .github/workflows/prod_deploy.yaml | 168 ++++++++++++++--------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index d4651f82..afc3bc01 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -14,34 +14,34 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation + # pytest: + # runs-on: ubuntu-latest + # name: pytest + + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation build_and_push: - needs: pytest + # needs: pytest runs-on: ubuntu-latest steps: @@ -69,62 +69,62 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - deploy: - name: Deploy changes on server - runs-on: ubuntu-latest - needs: [pytest, build_and_push] - environment: - name: prod_deploy - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Delete stage & dev - run: | - rm -r infra/stage - rm -r infra/dev - - - name: Copy infra via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/" - target: "${{ env.DEPLOY_PATH }}/infra" - rm: true - strip_components: 1 + # deploy: + # name: Deploy changes on server + # runs-on: ubuntu-latest + # needs: [pytest, build_and_push] + # environment: + # name: prod_deploy + # steps: + # - name: Checkout repository + # uses: actions/checkout@v4 + + # - name: Delete stage & dev + # run: | + # rm -r infra/stage + # rm -r infra/dev + + # - name: Copy infra via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/" + # target: "${{ env.DEPLOY_PATH }}/infra" + # rm: true + # strip_components: 1 - - name: Execute commands on VPS - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - script: | - cd ${{ env.DEPLOY_PATH }} - touch .env - - echo "${{ secrets.ENV_FILE }}" > .env - - cd infra/prod/ - sudo systemctl stop adaptive_hockey_federation.service - docker system prune --force - - sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - sudo systemctl daemon-reload - sudo systemctl start adaptive_hockey_federation.service - - sudo systemctl is-active --quiet adaptive_hockey_federation.service - until [ $? -eq 0 ]; do - echo "Waiting for adaptive_hockey_federation.service to be active..." - sleep 5 - sudo systemctl is-active --quiet adaptive_hockey_federation.service - done - - echo "adaptive_hockey_federation.service is active" - - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate + # - name: Execute commands on VPS + # uses: appleboy/ssh-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # script: | + # cd ${{ env.DEPLOY_PATH }} + # touch .env + + # echo "${{ secrets.ENV_FILE }}" > .env + + # cd infra/prod/ + # sudo systemctl stop adaptive_hockey_federation.service + # docker system prune --force + + # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + # sudo systemctl daemon-reload + # sudo systemctl start adaptive_hockey_federation.service + + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # until [ $? -eq 0 ]; do + # echo "Waiting for adaptive_hockey_federation.service to be active..." + # sleep 5 + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # done + + # echo "adaptive_hockey_federation.service is active" + + # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + # docker exec adaptive_hockey_federation python manage.py migrate From ad14fbdd746dbf1af54381a9a38dfc34808e56eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 19:23:33 +0500 Subject: [PATCH 098/126] test build3 --- .github/workflows/prod_deploy.yaml | 168 ++++++++++++++--------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index afc3bc01..d4651f82 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -14,34 +14,34 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation + pytest: + runs-on: ubuntu-latest + name: pytest + + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation build_and_push: - # needs: pytest + needs: pytest runs-on: ubuntu-latest steps: @@ -69,62 +69,62 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - # deploy: - # name: Deploy changes on server - # runs-on: ubuntu-latest - # needs: [pytest, build_and_push] - # environment: - # name: prod_deploy - # steps: - # - name: Checkout repository - # uses: actions/checkout@v4 - - # - name: Delete stage & dev - # run: | - # rm -r infra/stage - # rm -r infra/dev - - # - name: Copy infra via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/" - # target: "${{ env.DEPLOY_PATH }}/infra" - # rm: true - # strip_components: 1 + deploy: + name: Deploy changes on server + runs-on: ubuntu-latest + needs: [pytest, build_and_push] + environment: + name: prod_deploy + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Delete stage & dev + run: | + rm -r infra/stage + rm -r infra/dev + + - name: Copy infra via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: "${{ env.DEPLOY_PATH }}/infra" + rm: true + strip_components: 1 - # - name: Execute commands on VPS - # uses: appleboy/ssh-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # script: | - # cd ${{ env.DEPLOY_PATH }} - # touch .env - - # echo "${{ secrets.ENV_FILE }}" > .env - - # cd infra/prod/ - # sudo systemctl stop adaptive_hockey_federation.service - # docker system prune --force - - # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - # sudo systemctl daemon-reload - # sudo systemctl start adaptive_hockey_federation.service - - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # until [ $? -eq 0 ]; do - # echo "Waiting for adaptive_hockey_federation.service to be active..." - # sleep 5 - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # done - - # echo "adaptive_hockey_federation.service is active" - - # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - # docker exec adaptive_hockey_federation python manage.py migrate + - name: Execute commands on VPS + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + script: | + cd ${{ env.DEPLOY_PATH }} + touch .env + + echo "${{ secrets.ENV_FILE }}" > .env + + cd infra/prod/ + sudo systemctl stop adaptive_hockey_federation.service + docker system prune --force + + sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + sudo systemctl daemon-reload + sudo systemctl start adaptive_hockey_federation.service + + sudo systemctl is-active --quiet adaptive_hockey_federation.service + until [ $? -eq 0 ]; do + echo "Waiting for adaptive_hockey_federation.service to be active..." + sleep 5 + sudo systemctl is-active --quiet adaptive_hockey_federation.service + done + + echo "adaptive_hockey_federation.service is active" + + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate From 3dd162a7da92db9c9996e2f94046a45956f36e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 20:52:50 +0500 Subject: [PATCH 099/126] test watchtower --- .github/workflows/prod_deploy.yaml | 168 ++++++++++++++--------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index d4651f82..afc3bc01 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -14,34 +14,34 @@ defaults: working-directory: . jobs: - pytest: - runs-on: ubuntu-latest - name: pytest - - steps: - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - poetry-version: 1.5.0 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - poetry install - - name: pytest - run: | - poetry run pytest - working-directory: adaptive_hockey_federation + # pytest: + # runs-on: ubuntu-latest + # name: pytest + + # steps: + # - name: Install Python + # uses: actions/setup-python@v4 + # with: + # python-version: 3.11 + + # - name: Install Poetry + # uses: snok/install-poetry@v1 + # with: + # poetry-version: 1.5.0 + + # - name: Check out the repo + # uses: actions/checkout@v4 + + # - name: Install dependencies + # run: | + # poetry install + # - name: pytest + # run: | + # poetry run pytest + # working-directory: adaptive_hockey_federation build_and_push: - needs: pytest + # needs: pytest runs-on: ubuntu-latest steps: @@ -69,62 +69,62 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - deploy: - name: Deploy changes on server - runs-on: ubuntu-latest - needs: [pytest, build_and_push] - environment: - name: prod_deploy - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Delete stage & dev - run: | - rm -r infra/stage - rm -r infra/dev - - - name: Copy infra via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - source: "infra/" - target: "${{ env.DEPLOY_PATH }}/infra" - rm: true - strip_components: 1 + # deploy: + # name: Deploy changes on server + # runs-on: ubuntu-latest + # needs: [pytest, build_and_push] + # environment: + # name: prod_deploy + # steps: + # - name: Checkout repository + # uses: actions/checkout@v4 + + # - name: Delete stage & dev + # run: | + # rm -r infra/stage + # rm -r infra/dev + + # - name: Copy infra via ssh + # uses: appleboy/scp-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # source: "infra/" + # target: "${{ env.DEPLOY_PATH }}/infra" + # rm: true + # strip_components: 1 - - name: Execute commands on VPS - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.SSH_PASSPHRASE }} - script: | - cd ${{ env.DEPLOY_PATH }} - touch .env - - echo "${{ secrets.ENV_FILE }}" > .env - - cd infra/prod/ - sudo systemctl stop adaptive_hockey_federation.service - docker system prune --force - - sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - sudo systemctl daemon-reload - sudo systemctl start adaptive_hockey_federation.service - - sudo systemctl is-active --quiet adaptive_hockey_federation.service - until [ $? -eq 0 ]; do - echo "Waiting for adaptive_hockey_federation.service to be active..." - sleep 5 - sudo systemctl is-active --quiet adaptive_hockey_federation.service - done - - echo "adaptive_hockey_federation.service is active" - - docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - docker exec adaptive_hockey_federation python manage.py migrate + # - name: Execute commands on VPS + # uses: appleboy/ssh-action@master + # with: + # host: ${{ secrets.HOST }} + # username: ${{ secrets.USERNAME }} + # key: ${{ secrets.SSH_PRIVATE_KEY }} + # passphrase: ${{ secrets.SSH_PASSPHRASE }} + # script: | + # cd ${{ env.DEPLOY_PATH }} + # touch .env + + # echo "${{ secrets.ENV_FILE }}" > .env + + # cd infra/prod/ + # sudo systemctl stop adaptive_hockey_federation.service + # docker system prune --force + + # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + # sudo systemctl daemon-reload + # sudo systemctl start adaptive_hockey_federation.service + + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # until [ $? -eq 0 ]; do + # echo "Waiting for adaptive_hockey_federation.service to be active..." + # sleep 5 + # sudo systemctl is-active --quiet adaptive_hockey_federation.service + # done + + # echo "adaptive_hockey_federation.service is active" + + # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + # docker exec adaptive_hockey_federation python manage.py migrate From 808d7b1d74e0d57450dc58a02c7b20cd266936b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 21:12:56 +0500 Subject: [PATCH 100/126] test watchtower1 --- .github/workflows/prod_deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index afc3bc01..b722ff09 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -69,6 +69,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + # deploy: # name: Deploy changes on server # runs-on: ubuntu-latest From d466f03f8c031b15d1db024f61c329b9df55d873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 21:37:10 +0500 Subject: [PATCH 101/126] on review3 --- .github/workflows/prod_deploy.yaml | 168 ++++++++++++++--------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index b722ff09..fb614471 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -14,34 +14,34 @@ defaults: working-directory: . jobs: - # pytest: - # runs-on: ubuntu-latest - # name: pytest - - # steps: - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: 3.11 - - # - name: Install Poetry - # uses: snok/install-poetry@v1 - # with: - # poetry-version: 1.5.0 - - # - name: Check out the repo - # uses: actions/checkout@v4 - - # - name: Install dependencies - # run: | - # poetry install - # - name: pytest - # run: | - # poetry run pytest - # working-directory: adaptive_hockey_federation + pytest: + runs-on: ubuntu-latest + name: pytest + + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation build_and_push: - # needs: pytest + needs: pytest runs-on: ubuntu-latest steps: @@ -70,62 +70,62 @@ jobs: labels: ${{ steps.meta.outputs.labels }} - # deploy: - # name: Deploy changes on server - # runs-on: ubuntu-latest - # needs: [pytest, build_and_push] - # environment: - # name: prod_deploy - # steps: - # - name: Checkout repository - # uses: actions/checkout@v4 - - # - name: Delete stage & dev - # run: | - # rm -r infra/stage - # rm -r infra/dev - - # - name: Copy infra via ssh - # uses: appleboy/scp-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # source: "infra/" - # target: "${{ env.DEPLOY_PATH }}/infra" - # rm: true - # strip_components: 1 + deploy: + name: Deploy changes on server + runs-on: ubuntu-latest + needs: [pytest, build_and_push] + environment: + name: prod_deploy + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Delete stage & dev + run: | + rm -r infra/stage + rm -r infra/dev + + - name: Copy infra via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: "${{ env.DEPLOY_PATH }}/infra" + rm: true + strip_components: 1 - # - name: Execute commands on VPS - # uses: appleboy/ssh-action@master - # with: - # host: ${{ secrets.HOST }} - # username: ${{ secrets.USERNAME }} - # key: ${{ secrets.SSH_PRIVATE_KEY }} - # passphrase: ${{ secrets.SSH_PASSPHRASE }} - # script: | - # cd ${{ env.DEPLOY_PATH }} - # touch .env - - # echo "${{ secrets.ENV_FILE }}" > .env - - # cd infra/prod/ - # sudo systemctl stop adaptive_hockey_federation.service - # docker system prune --force - - # sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service - # sudo systemctl daemon-reload - # sudo systemctl start adaptive_hockey_federation.service - - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # until [ $? -eq 0 ]; do - # echo "Waiting for adaptive_hockey_federation.service to be active..." - # sleep 5 - # sudo systemctl is-active --quiet adaptive_hockey_federation.service - # done - - # echo "adaptive_hockey_federation.service is active" - - # docker exec adaptive_hockey_federation python manage.py collectstatic --noinput - # docker exec adaptive_hockey_federation python manage.py migrate + - name: Execute commands on VPS + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + script: | + cd ${{ env.DEPLOY_PATH }} + touch .env + + echo "${{ secrets.ENV_FILE }}" > .env + + cd infra/prod/ + sudo systemctl stop adaptive_hockey_federation.service + docker system prune --force + + sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + sudo systemctl daemon-reload + sudo systemctl start adaptive_hockey_federation.service + + sudo systemctl is-active --quiet adaptive_hockey_federation.service + until [ $? -eq 0 ]; do + echo "Waiting for adaptive_hockey_federation.service to be active..." + sleep 5 + sudo systemctl is-active --quiet adaptive_hockey_federation.service + done + + echo "adaptive_hockey_federation.service is active" + + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate From 251bde84b88aff6dfae3eee21ce3660143666249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sat, 28 Sep 2024 22:17:09 +0500 Subject: [PATCH 102/126] on review4 --- .github/workflows/prod_deploy.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index fb614471..35366d27 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,9 +1,13 @@ name: Production deploy on: - push: + workflow_run: + workflows: + - Build and push Docker image + types: + - completed branches: - - feature/convert_ci-cd_to_the_prod + - master env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} From 9a125d73d104e8293ece782f3540727137cd2147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sun, 29 Sep 2024 15:58:51 +0500 Subject: [PATCH 103/126] test build --- .github/workflows/prod_deploy.yaml | 33 +++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 35366d27..002f5793 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,13 +1,9 @@ name: Production deploy on: - workflow_run: - workflows: - - Build and push Docker image - types: - - completed + push: branches: - - master + - feature/convert_ci-cd_to_the_prod env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} @@ -44,28 +40,28 @@ jobs: poetry run pytest working-directory: adaptive_hockey_federation - build_and_push: + build: needs: pytest runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@v4 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + - name: Log in to the Container registry + uses: docker/login-action@v3.0.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker + + - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - uses: docker/build-push-action@v5 + + - name: Build and push Docker image + uses: docker/build-push-action@v5.3.0 with: context: . file: infra/prod/prod.Dockerfile @@ -73,11 +69,10 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - deploy: name: Deploy changes on server runs-on: ubuntu-latest - needs: [pytest, build_and_push] + needs: pytest environment: name: prod_deploy steps: From 0eb325c056e7863536b4703843da4e6f35287ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sun, 29 Sep 2024 16:07:06 +0500 Subject: [PATCH 104/126] test build1 --- .github/workflows/prod_deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 002f5793..35f9aaeb 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -4,6 +4,7 @@ on: push: branches: - feature/convert_ci-cd_to_the_prod + env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} @@ -71,8 +72,8 @@ jobs: deploy: name: Deploy changes on server + needs: [pytest, build] runs-on: ubuntu-latest - needs: pytest environment: name: prod_deploy steps: From 114b9c99e32017f78fcbcdd31abaa4253c05de53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sun, 29 Sep 2024 16:53:26 +0500 Subject: [PATCH 105/126] on review 4 --- .github/workflows/prod_deploy.yaml | 43 +++++++----------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 35f9aaeb..0127e88c 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,13 +1,16 @@ name: Production deploy on: - push: + workflow_run: + workflows: + - Build and push Docker image + types: + - completed branches: - - feature/convert_ci-cd_to_the_prod + - master + env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} DEPLOY_PATH: adaptive_hockey_federation defaults: @@ -41,44 +44,18 @@ jobs: poetry run pytest working-directory: adaptive_hockey_federation - build: - needs: pytest - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to the Container registry - uses: docker/login-action@v3.0.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - uses: docker/build-push-action@v5.3.0 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy changes on server - needs: [pytest, build] + needs: pytest runs-on: ubuntu-latest environment: name: prod_deploy steps: - name: Checkout repository uses: actions/checkout@v4 + with: + ref: master - name: Delete stage & dev run: | From eac00c7c33b8d9fa1afbdddea9f3699a20d5d996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sun, 29 Sep 2024 21:09:19 +0500 Subject: [PATCH 106/126] test build-1 --- .../build-and-push-github-packages.yaml | 2 +- .github/workflows/prod_deploy.yaml | 47 ++++++++++++++++--- .../core/config/prod_settings.py | 16 ++++--- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml index 309604e9..d1d7d827 100644 --- a/.github/workflows/build-and-push-github-packages.yaml +++ b/.github/workflows/build-and-push-github-packages.yaml @@ -31,7 +31,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/feature/master' + if: github.ref == 'refs/heads/master' uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 0127e88c..3750edf3 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,17 +1,22 @@ name: Production deploy on: - workflow_run: - workflows: - - Build and push Docker image - types: - - completed + # workflow_run: + # workflows: + # - Build and push Docker image + # types: + # - completed + # branches: + # - master + push: branches: - - master + - feature/convert_ci-cd_to_the_prod env: DEPLOY_PATH: adaptive_hockey_federation + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} defaults: run: @@ -44,10 +49,38 @@ jobs: poetry run pytest working-directory: adaptive_hockey_federation + build_and_push: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + deploy: name: Deploy changes on server - needs: pytest + needs: [pytest, build_and_push] runs-on: ubuntu-latest environment: name: prod_deploy diff --git a/adaptive_hockey_federation/core/config/prod_settings.py b/adaptive_hockey_federation/core/config/prod_settings.py index 5d8cf3c1..df312ba0 100644 --- a/adaptive_hockey_federation/core/config/prod_settings.py +++ b/adaptive_hockey_federation/core/config/prod_settings.py @@ -10,17 +10,19 @@ DATABASES = { "default": { - "ENGINE": env("DB_ENGINE", default="django.db.backends.postgresql"), - "NAME": env("POSTGRES_DB", default="postgres_db"), - "USER": env("POSTGRES_USER", default="postgres_user"), - "PASSWORD": env("POSTGRES_PASSWORD", default="postgres_password"), - "HOST": env("DB_HOST", default="localhost"), - "PORT": env("DB_PORT", default="5432"), + "ENGINE": env("DB_ENGINE"), + "NAME": env("POSTGRES_DB"), + "USER": env("POSTGRES_USER"), + "PASSWORD": env("POSTGRES_PASSWORD"), + "HOST": env("DB_HOST"), + "PORT": env("DB_PORT"), } } - +# TODO: возможно удаления т.к относится к parser FIXSTURES_DIR = BASE_DIR / "core" / "fixtures" +# TODO: возможно удаления т.к относится к parser JSON_PARSER_FILE = "data.json" +# TODO: возможно удаления т.к относится к parser FIXSTURES_FILE = FIXSTURES_DIR / JSON_PARSER_FILE RESOURSES_ROOT = BASE_DIR / "resourses" From a7562be8d186fb201fd355531840760a4bb95038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Sun, 29 Sep 2024 21:15:35 +0500 Subject: [PATCH 107/126] test build-2 --- .github/workflows/prod_deploy.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 3750edf3..2669c811 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -51,6 +51,7 @@ jobs: build_and_push: runs-on: ubuntu-latest + needs: pytest steps: - uses: actions/checkout@v3 @@ -87,8 +88,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - with: - ref: master - name: Delete stage & dev run: | From 9a7f6fe451c1c6ff4ca9f2d89328cded66a63c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 00:10:40 +0500 Subject: [PATCH 108/126] test build 5 --- .../core/config/base_settings.py | 1 + .../core/config/dev_settings.py | 2 +- adaptive_hockey_federation/manage.py | 18 +++++++++++++++++- requirements/production.txt | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/adaptive_hockey_federation/core/config/base_settings.py b/adaptive_hockey_federation/core/config/base_settings.py index ba753a99..20d55d64 100644 --- a/adaptive_hockey_federation/core/config/base_settings.py +++ b/adaptive_hockey_federation/core/config/base_settings.py @@ -1,4 +1,5 @@ from pathlib import Path +from re import DEBUG import environ from django.contrib.messages import constants as messages diff --git a/adaptive_hockey_federation/core/config/dev_settings.py b/adaptive_hockey_federation/core/config/dev_settings.py index 446035cb..89eae34e 100644 --- a/adaptive_hockey_federation/core/config/dev_settings.py +++ b/adaptive_hockey_federation/core/config/dev_settings.py @@ -1,4 +1,4 @@ -from . base_settings import * +from .base_settings import * ROOT_DIR = BASE_DIR.parent diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index d730851a..cb57f050 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -3,12 +3,28 @@ import os import sys +import environ + +from adaptive_hockey_federation.core.config.base_settings import BASE_DIR + +env = environ.Env() + + +env.read_env(BASE_DIR.parent / ".env") + +DEBUG = os.environ.get("DEBUG") + + +print(f"{DEBUG=}") + +switch_prod_dev = {"True": "dev", "False": "prod"} +print(switch_prod_dev[str(DEBUG)]) def main(): """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", - "core.config.prod_settings") + f"core.config.{switch_prod_dev[str(DEBUG)]}_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/requirements/production.txt b/requirements/production.txt index b28b80a2..94721d7e 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -9,6 +9,7 @@ click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" +django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" django==4.2.13 ; python_version >= "3.11" and python_version < "4.0" djangorestframework==3.15.2 ; python_version >= "3.11" and python_version < "4.0" From 7015852d3a003641262c34396a40dea1289499cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 00:23:54 +0500 Subject: [PATCH 109/126] test build 6 --- .../core/config/base_settings.py | 1 - adaptive_hockey_federation/manage.py | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/adaptive_hockey_federation/core/config/base_settings.py b/adaptive_hockey_federation/core/config/base_settings.py index 20d55d64..ba753a99 100644 --- a/adaptive_hockey_federation/core/config/base_settings.py +++ b/adaptive_hockey_federation/core/config/base_settings.py @@ -1,5 +1,4 @@ from pathlib import Path -from re import DEBUG import environ from django.contrib.messages import constants as messages diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index cb57f050..1cd41d10 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -5,26 +5,19 @@ import sys import environ -from adaptive_hockey_federation.core.config.base_settings import BASE_DIR env = environ.Env() - -env.read_env(BASE_DIR.parent / ".env") - DEBUG = os.environ.get("DEBUG") - -print(f"{DEBUG=}") - switch_prod_dev = {"True": "dev", "False": "prod"} -print(switch_prod_dev[str(DEBUG)]) def main(): """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", - f"core.config.{switch_prod_dev[str(DEBUG)]}_settings") + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + f"core.config.{switch_prod_dev[str(DEBUG)]}_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: From f5b44b760f1a2fff19ece4880285f95e8215f843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 00:46:07 +0500 Subject: [PATCH 110/126] test build 6 --- adaptive_hockey_federation/manage.py | 10 +--------- requirements/production.txt | 1 + 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index 1cd41d10..2c2dc592 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -3,21 +3,13 @@ import os import sys -import environ - - -env = environ.Env() - -DEBUG = os.environ.get("DEBUG") - -switch_prod_dev = {"True": "dev", "False": "prod"} def main(): """Run administrative tasks.""" os.environ.setdefault( "DJANGO_SETTINGS_MODULE", - f"core.config.{switch_prod_dev[str(DEBUG)]}_settings") + "core.config.dev_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/requirements/production.txt b/requirements/production.txt index 94721d7e..6cb07cd8 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -8,6 +8,7 @@ click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") +django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" From ff13ebe4ad4d82d9d76d0229ed11583deb29531b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 00:52:41 +0500 Subject: [PATCH 111/126] test build 7 --- requirements/production.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/production.txt b/requirements/production.txt index 6cb07cd8..f84ab095 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -36,7 +36,7 @@ pytest==8.2.2 ; python_version >= "3.11" and python_version < "4.0" python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0" pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" -redis==5.0.7 ; python_version >= "3.11" and python_version < "4.0" +requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" From 090e16fba576be86d723c4c94569374fd5eb522b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 00:57:48 +0500 Subject: [PATCH 112/126] test build 8 --- requirements/production.txt | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/requirements/production.txt b/requirements/production.txt index f84ab095..d5114892 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -1,50 +1,98 @@ amqp==5.2.0 ; python_version >= "3.11" and python_version < "4.0" +annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0" +anyio==4.4.0 ; python_version >= "3.11" and python_version < "4.0" asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" async-timeout==4.0.3 ; python_version >= "3.11" and python_full_version < "3.11.3" attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" billiard==4.2.0 ; python_version >= "3.11" and python_version < "4.0" +certifi==2024.6.2 ; python_version >= "3.11" and python_version < "4.0" +cfgv==3.4.0 ; python_version >= "3.11" and python_version < "4.0" click-didyoumean==0.3.1 ; python_version >= "3.11" and python_version < "4.0" click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") +distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" django==4.2.13 ; python_version >= "3.11" and python_version < "4.0" djangorestframework==3.15.2 ; python_version >= "3.11" and python_version < "4.0" +dnspython==2.6.1 ; python_version >= "3.11" and python_version < "4.0" drf-yasg==1.21.7 ; python_version >= "3.11" and python_version < "4.0" +email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0" et-xmlfile==1.1.0 ; python_version >= "3.11" and python_version < "4.0" +factory-boy==3.3.0 ; python_version >= "3.11" and python_version < "4.0" +faker==26.0.0 ; python_version >= "3.11" and python_version < "4.0" +fastapi-cli[standard]==0.0.5 ; python_version >= "3.11" and python_version < "4.0" +fastapi[standard]==0.112.0 ; python_version >= "3.11" and python_version < "4.0" +filelock==3.15.4 ; python_version >= "3.11" and python_version < "4.0" gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0" +h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" +httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0" +httptools==0.6.1 ; python_version >= "3.11" and python_version < "4.0" +httpx==0.27.0 ; python_version >= "3.11" and python_version < "4.0" +humanize==4.9.0 ; python_version >= "3.11" and python_version < "4.0" +identify==2.5.36 ; python_version >= "3.11" and python_version < "4.0" +idna==3.7 ; python_version >= "3.11" and python_version < "4.0" inflection==0.5.1 ; python_version >= "3.11" and python_version < "4.0" iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +jinja2==3.1.4 ; python_version >= "3.11" and python_version < "4.0" kombu==5.3.7 ; python_version >= "3.11" and python_version < "4.0" +markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0" +markupsafe==2.1.5 ; python_version >= "3.11" and python_version < "4.0" +mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0" mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0" mypy==1.10.1 ; python_version >= "3.11" and python_version < "4.0" +nodeenv==1.9.1 ; python_version >= "3.11" and python_version < "4.0" +numpy==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +opencv-python-headless==4.10.0.84 ; python_version >= "3.11" and python_version < "4.0" openpyxl-stubs==0.1.25 ; python_version >= "3.11" and python_version < "4.0" openpyxl==3.1.5 ; python_version >= "3.11" and python_version < "4.0" packaging==24.1 ; python_version >= "3.11" and python_version < "4.0" phonenumbers==8.13.39 ; python_version >= "3.11" and python_version < "4.0" pillow==10.3.0 ; python_version >= "3.11" and python_version < "4.0" +platformdirs==4.2.2 ; python_version >= "3.11" and python_version < "4.0" pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0" +pre-commit==3.5.0 ; python_version >= "3.11" and python_version < "4.0" +prometheus-client==0.20.0 ; python_version >= "3.11" and python_version < "4.0" prompt-toolkit==3.0.47 ; python_version >= "3.11" and python_version < "4.0" psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0" +pydantic-core==2.20.1 ; python_version >= "3.11" and python_version < "4.0" +pydantic==2.8.2 ; python_version >= "3.11" and python_version < "4.0" +pygments==2.18.0 ; python_version >= "3.11" and python_version < "4.0" pytest-django==4.8.0 ; python_version >= "3.11" and python_version < "4.0" pytest-subtests==0.12.1 ; python_version >= "3.11" and python_version < "4.0" pytest==8.2.2 ; python_version >= "3.11" and python_version < "4.0" python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0" +python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" +python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0" pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" +rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0" requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" +ruff==0.4.10 ; python_version >= "3.11" and python_version < "4.0" +setuptools==70.1.1 ; python_version >= "3.11" and python_version < "4.0" +shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0" six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" +sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" +starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0" +tornado==6.4.1 ; python_version >= "3.11" and python_version < "4.0" +typer==0.12.3 ; python_version >= "3.11" and python_version < "4.0" types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" +types-pillow==10.2.0.20240520 ; python_version >= "3.11" and python_version < "4.0" types-python-dateutil==2.9.0.20240316 ; python_version >= "3.11" and python_version < "4.0" typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0" tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" uritemplate==4.1.1 ; python_version >= "3.11" and python_version < "4.0" +uvicorn[standard]==0.30.5 ; python_version >= "3.11" and python_version < "4.0" +uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0" vine==5.1.0 ; python_version >= "3.11" and python_version < "4.0" +virtualenv==20.26.3 ; python_version >= "3.11" and python_version < "4.0" +watchfiles==0.23.0 ; python_version >= "3.11" and python_version < "4.0" wcwidth==0.2.13 ; python_version >= "3.11" and python_version < "4.0" +websockets==12.0 ; python_version >= "3.11" and python_version < "4.0" wrapt==1.16.0 ; python_version >= "3.11" and python_version < "4.0" yadisk==3.1.0 ; python_version >= "3.11" and python_version < "4.0" From a28a143bc26388279b2ac082968d55e63a277656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 18:52:25 +0500 Subject: [PATCH 113/126] test build 8 --- .../core/config/prod_settings.py | 2 ++ adaptive_hockey_federation/core/utils.py | 11 +++++++++++ adaptive_hockey_federation/core/wsgi.py | 3 ++- adaptive_hockey_federation/manage.py | 3 ++- requirements/production.txt | 2 +- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/adaptive_hockey_federation/core/config/prod_settings.py b/adaptive_hockey_federation/core/config/prod_settings.py index df312ba0..3431031f 100644 --- a/adaptive_hockey_federation/core/config/prod_settings.py +++ b/adaptive_hockey_federation/core/config/prod_settings.py @@ -4,6 +4,8 @@ env.read_env(ROOT_DIR / ".env") +DEBUG = False + INTERNAL_IPS = [ "127.0.0.1", ] diff --git a/adaptive_hockey_federation/core/utils.py b/adaptive_hockey_federation/core/utils.py index c082279a..db9f213a 100644 --- a/adaptive_hockey_federation/core/utils.py +++ b/adaptive_hockey_federation/core/utils.py @@ -1,7 +1,9 @@ import os from datetime import datetime +from re import DEBUG from typing import Any, List, Optional + from core.constants import AgeLimits, FileConstants, TimeFormat from core.settings.openpyxl_settings import ( ALIGNMENT_CENTER, @@ -179,3 +181,12 @@ def save_workbook(wb, filename): wb.save(file_path) return filename_with_timestamp + + +def switch_prod_dev() -> str: + DEBUG = os.environ.get("DEBUG", default=False) + print(f"DEBUG: {DEBUG}") + if DEBUG: + return "dev" + else: + return "prod" diff --git a/adaptive_hockey_federation/core/wsgi.py b/adaptive_hockey_federation/core/wsgi.py index b83e55d6..dee99fc9 100644 --- a/adaptive_hockey_federation/core/wsgi.py +++ b/adaptive_hockey_federation/core/wsgi.py @@ -3,8 +3,9 @@ from django.core.wsgi import get_wsgi_application os.environ.setdefault( + # при разработке core.config.dev_settings "DJANGO_SETTINGS_MODULE", - "core.config.dev_settings", + "core.config.prod_settings", ) application = get_wsgi_application() diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index 2c2dc592..a0a4045f 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -7,9 +7,10 @@ def main(): """Run administrative tasks.""" + # при разработке core.config.dev_settings os.environ.setdefault( "DJANGO_SETTINGS_MODULE", - "core.config.dev_settings") + "core.config.prod_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/requirements/production.txt b/requirements/production.txt index d5114892..8611e41e 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -13,7 +13,7 @@ click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" -django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" +# django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" From ba30530eaf2a8acc34e3c429bbf2dbcb68bd75f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 18:53:04 +0500 Subject: [PATCH 114/126] test build 9 --- adaptive_hockey_federation/core/utils.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/adaptive_hockey_federation/core/utils.py b/adaptive_hockey_federation/core/utils.py index db9f213a..3f8b2794 100644 --- a/adaptive_hockey_federation/core/utils.py +++ b/adaptive_hockey_federation/core/utils.py @@ -1,6 +1,5 @@ import os from datetime import datetime -from re import DEBUG from typing import Any, List, Optional @@ -181,12 +180,3 @@ def save_workbook(wb, filename): wb.save(file_path) return filename_with_timestamp - - -def switch_prod_dev() -> str: - DEBUG = os.environ.get("DEBUG", default=False) - print(f"DEBUG: {DEBUG}") - if DEBUG: - return "dev" - else: - return "prod" From 40c6df238f51bd36abead8d41d75f52bcfc382b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 19:08:22 +0500 Subject: [PATCH 115/126] test build 10 --- requirements/production.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/production.txt b/requirements/production.txt index 8611e41e..d5114892 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -13,7 +13,7 @@ click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" -# django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" +django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" From 2a942352d522db95ee270f22abbac8a88de5e46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 19:28:47 +0500 Subject: [PATCH 116/126] test build 11 --- .../build-and-push-github-packages.yaml | 51 ------- .github/workflows/prod_deploy.yaml | 10 +- .github/workflows/stage_deploy.yaml | 129 ------------------ 3 files changed, 1 insertion(+), 189 deletions(-) delete mode 100644 .github/workflows/build-and-push-github-packages.yaml delete mode 100644 .github/workflows/stage_deploy.yaml diff --git a/.github/workflows/build-and-push-github-packages.yaml b/.github/workflows/build-and-push-github-packages.yaml deleted file mode 100644 index d1d7d827..00000000 --- a/.github/workflows/build-and-push-github-packages.yaml +++ /dev/null @@ -1,51 +0,0 @@ -name: Build and push Docker image - -on: - push: - branches: - - master - - dev - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build_and_push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image for Production - if: github.ref == 'refs/heads/master' - uses: docker/build-push-action@v5 - with: - context: . - file: infra/prod/prod.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - - name: Build and push Docker image for Stage - if: github.ref == 'refs/heads/dev' - uses: docker/build-push-action@v5 - with: - context: . - file: infra/stage/stage.Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 2669c811..12e6a1cf 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -1,18 +1,10 @@ name: Production deploy on: - # workflow_run: - # workflows: - # - Build and push Docker image - # types: - # - completed - # branches: - # - master push: branches: - - feature/convert_ci-cd_to_the_prod + - feature-convert_ci-cd_to_the_prod - env: DEPLOY_PATH: adaptive_hockey_federation REGISTRY: ghcr.io diff --git a/.github/workflows/stage_deploy.yaml b/.github/workflows/stage_deploy.yaml deleted file mode 100644 index b0056148..00000000 --- a/.github/workflows/stage_deploy.yaml +++ /dev/null @@ -1,129 +0,0 @@ -# name: Project stage deploy - -# on: -# workflow_run: -# workflows: -# - Build and push Docker image -# types: -# - completed - -# env: -# REGISTRY: ghcr.io -# IMAGE_NAME: adaptive_hockey_federation -# DEPLOY_PATH: adaptive_hockey_federation - -# defaults: -# run: -# working-directory: . - -# jobs: -# pytest: -# runs-on: ubuntu-latest -# name: pytest -# steps: -# - name: Install Python -# uses: actions/setup-python@v4 -# with: -# python-version: 3.11 - -# - name: Install Poetry -# uses: snok/install-poetry@v1 -# with: -# poetry-version: 1.5.0 - -# - name: Check out the repo -# uses: actions/checkout@v4 - -# - name: Install dependencies -# run: | -# poetry install -# - name: pytest -# run: | -# poetry run pytest -# working-directory: adaptive_hockey_federation - -# deploy: -# name: Deploy changes on server -# runs-on: ubuntu-latest -# environment: -# name: stage_deploy -# needs: pytest -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# with: -# ref: dev - -# - name: Set up SSH -# run: | -# mkdir -p ~/.ssh -# chmod 700 ~/.ssh -# ssh-keyscan -H ${{ secrets.HOST }} > ~/.ssh/known_hosts -# chmod 644 ~/.ssh/known_hosts -# echo "${{ secrets.TEST_RSA_SECRET_KEY }}" > ~/.ssh/id_rsa -# chmod 600 ~/.ssh/id_rsa -# - name: Create folder for deploy -# run: ssh -vvv ${{ secrets.USERNAME }}@${{ secrets.HOST }} mkdir -p ${{ env.DEPLOY_PATH }}/infra - -# - name: Copy dev folder to VPS -# run: | -# scp -r $GITHUB_WORKSPACE/infra/stage/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ -# scp -r $GITHUB_WORKSPACE/infra/nginx/ ${{ secrets.USERNAME }}@${{ secrets.HOST }}:${{ env.DEPLOY_PATH }}/infra/ - -# - name: Execute commands on VPS -# uses: appleboy/ssh-action@master -# with: -# host: ${{ secrets.HOST }} -# username: ${{ secrets.USERNAME }} -# key: ${{ secrets.TEST_RSA_SECRET_KEY }} -# script: | -# cd ${{ env.DEPLOY_PATH }} -# rm .env -# touch .env - -# echo HOST=${{ secrets.HOST }} >> .env -# echo PORT=${{ secrets.PORT }} >> .env -# echo IMAGE_COMPOSE=${{ secrets.IMAGE_COMPOSE }} >> .env -# echo ST=${{ secrets.ST }} >> .env - -# echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env -# echo DEBUG=${{ secrets.DEBUG }} >> .env -# echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} >> .env -# echo CSRF_TRUSTED_ORIGINS=${{ secrets.CSRF_TRUSTED_ORIGINS }} >> .env - -# echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env -# echo POSTGRES_DB=${{ secrets.POSTGRES_DB }} >> .env -# echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env -# echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env -# echo DB_HOST=${{ secrets.DB_HOST }} >> .env -# echo DB_PORT=${{ secrets.DB_PORT }} >> .env - -# echo EMAIL_BACKEND=${{ secrets.EMAIL_BACKEND }} >> .env -# echo EMAIL_HOST=${{ secrets.EMAIL_HOST }} >> .env -# echo EMAIL_PORT=${{ secrets.EMAIL_PORT }} >> .env -# echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env -# echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env -# echo EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }} >> .env - -# # TODO Добавить копирование переменных с конфигами для Celery и Redis - -# cd infra/stage/ -# sudo systemctl stop adaptive_hockey_federation.service -# docker system prune --force - -# # Installing defend service for app -# sudo cp -f /home/developer/adaptive_hockey_federation/infra/stage/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service -# sudo systemctl daemon-reload -# sudo systemctl start adaptive_hockey_federation.service - -# sudo systemctl is-active --quiet adaptive_hockey_federation.service -# until [ $? -eq 0 ]; do -# echo "Waiting for adaptive_hockey_federation.service to be active..." -# sleep 5 -# sudo systemctl is-active --quiet adaptive_hockey_federation.service -# done - -# echo "adaptive_hockey_federation.service is active" - -# docker exec adaptive_hockey_federation python manage.py collectstatic --noinput -# docker exec adaptive_hockey_federation python manage.py migrate From 92f3e719a8889e3778a7a29389a4779acc4c85eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 19:30:03 +0500 Subject: [PATCH 117/126] test build 12 --- .github/workflows/prod_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 12e6a1cf..e20a8347 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - feature-convert_ci-cd_to_the_prod + - feature/convert_ci-cd_to_the_prod env: DEPLOY_PATH: adaptive_hockey_federation From 84d79addec9f015dd470efe60d13bd5ceefaed1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 19:39:05 +0500 Subject: [PATCH 118/126] test build 14 --- infra/prod/prod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile index 749cfb42..86b91607 100644 --- a/infra/prod/prod.Dockerfile +++ b/infra/prod/prod.Dockerfile @@ -9,4 +9,4 @@ COPY . . WORKDIR /app/adaptive_hockey_federation -CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] +CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000", "--workers", "5"] From a9412884791fbb00c3e020d22d9b53eb467a34ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Mon, 30 Sep 2024 19:58:10 +0500 Subject: [PATCH 119/126] on_review --- .github/workflows/prod_deploy.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index e20a8347..5819b0bf 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -3,7 +3,7 @@ name: Production deploy on: push: branches: - - feature/convert_ci-cd_to_the_prod + - master env: DEPLOY_PATH: adaptive_hockey_federation @@ -18,7 +18,6 @@ jobs: pytest: runs-on: ubuntu-latest name: pytest - steps: - name: Install Python uses: actions/setup-python@v4 From 3122a24095aa5a4afabeda3ecc281c175918cee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 1 Oct 2024 17:03:50 +0500 Subject: [PATCH 120/126] on_review final --- adaptive_hockey_federation/core/wsgi.py | 1 - adaptive_hockey_federation/manage.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/adaptive_hockey_federation/core/wsgi.py b/adaptive_hockey_federation/core/wsgi.py index dee99fc9..bae01bc0 100644 --- a/adaptive_hockey_federation/core/wsgi.py +++ b/adaptive_hockey_federation/core/wsgi.py @@ -3,7 +3,6 @@ from django.core.wsgi import get_wsgi_application os.environ.setdefault( - # при разработке core.config.dev_settings "DJANGO_SETTINGS_MODULE", "core.config.prod_settings", ) diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py index a0a4045f..2c2dc592 100644 --- a/adaptive_hockey_federation/manage.py +++ b/adaptive_hockey_federation/manage.py @@ -7,10 +7,9 @@ def main(): """Run administrative tasks.""" - # при разработке core.config.dev_settings os.environ.setdefault( "DJANGO_SETTINGS_MODULE", - "core.config.prod_settings") + "core.config.dev_settings") try: from django.core.management import execute_from_command_line except ImportError as exc: From 5f925c3564a0ce922ca09886e9f5cc3c21486c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 1 Oct 2024 17:07:50 +0500 Subject: [PATCH 121/126] on_review final --- .github/workflows/prod_deploy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 5819b0bf..3f8fab04 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -41,6 +41,7 @@ jobs: working-directory: adaptive_hockey_federation build_and_push: + if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest needs: pytest @@ -71,6 +72,7 @@ jobs: deploy: + if: github.ref == 'refs/heads/master' name: Deploy changes on server needs: [pytest, build_and_push] runs-on: ubuntu-latest From 6b390d30d713a7fc93cbe8c8f294a5c6970853f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 1 Oct 2024 17:50:12 +0500 Subject: [PATCH 122/126] on_review final1 --- .github/workflows/ codestyle_pep8.yaml | 2 +- .github/workflows/pytest.yaml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ codestyle_pep8.yaml b/.github/workflows/ codestyle_pep8.yaml index 6d60526b..40561aad 100644 --- a/.github/workflows/ codestyle_pep8.yaml +++ b/.github/workflows/ codestyle_pep8.yaml @@ -1,4 +1,4 @@ -name: CI +name: Codestyle_pep8 on: [push, pull_request] diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index bd9d9bc5..1beb24f4 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -1,6 +1,10 @@ -name: CI +name: Pytest -on: [pull_request] +on: + push: + branches: + - dev + - feature/* jobs: pytest: From 74e89cf3ef6ee61d4a2f62fe906d5c27ac585062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD?= <128288828+InKLaR1TY@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:46:39 +0300 Subject: [PATCH 123/126] Remove parser and rewrite command fill-db (#554) --- .pre-commit-config.yaml | 4 +- .../core/management/commands/fill-db.py | 155 ++++-- adaptive_hockey_federation/parser/__init__.py | 0 .../parser/docx_parser.py | 517 ------------------ .../parser/exception.py | 2 - .../parser/importing_db.py | 261 --------- adaptive_hockey_federation/parser/parser.py | 108 ---- .../parser/user_card.py | 46 -- .../parser/xlsx_parser.py | 31 -- pyproject.toml | 4 - requirements/develop.txt | 23 +- requirements/production.txt | 18 +- 12 files changed, 111 insertions(+), 1058 deletions(-) delete mode 100644 adaptive_hockey_federation/parser/__init__.py delete mode 100644 adaptive_hockey_federation/parser/docx_parser.py delete mode 100644 adaptive_hockey_federation/parser/exception.py delete mode 100644 adaptive_hockey_federation/parser/importing_db.py delete mode 100644 adaptive_hockey_federation/parser/parser.py delete mode 100644 adaptive_hockey_federation/parser/user_card.py delete mode 100644 adaptive_hockey_federation/parser/xlsx_parser.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fdcc6f2c..b60491fa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-docstring-first - id: check-merge-conflict @@ -8,7 +8,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.5 + rev: v0.6.8 hooks: - id: ruff exclude: migrations/|config/|tests/|.*settings(\.py|/)? diff --git a/adaptive_hockey_federation/core/management/commands/fill-db.py b/adaptive_hockey_federation/core/management/commands/fill-db.py index 9b5cd617..b5ba070b 100644 --- a/adaptive_hockey_federation/core/management/commands/fill-db.py +++ b/adaptive_hockey_federation/core/management/commands/fill-db.py @@ -1,18 +1,15 @@ +import json + +from django.apps import apps from django.core.management.base import BaseCommand +from django.db import connection, transaction +from main.models import Diagnosis from adaptive_hockey_federation.core.config.dev_settings import ( FILE_MODEL_MAP, FIXSTURES_DIR, - FIXSTURES_FILE, -) -from adaptive_hockey_federation.parser.importing_db import ( - clear_data_db, - importing_parser_data_db, - importing_real_data_db, ) -DB_MESSAGE = "Данные успешно добавлены!" - class Command(BaseCommand): """Класс для парсинга данных и их записи в БД.""" @@ -20,13 +17,7 @@ class Command(BaseCommand): help = "Запуск парсера офисных документов, и запись их в БД." def add_arguments(self, parser): - """Добавляет новые аргументы для командной строки.""" - parser.add_argument( - "-p", - "--parser", - action="store_true", - help="Запуск парсера документов", - ) + """Аргументы.""" parser.add_argument( "-f", "--fixtures", @@ -34,50 +25,116 @@ def add_arguments(self, parser): help="Фикстуры с реальными данными для таблиц.", ) - def load_data(self) -> None: - """Загрузка распарсенных данных.""" - importing_parser_data_db(FIXSTURES_FILE) - return None - - def load_real_data(self) -> None: + @transaction.atomic + def load_real_data(self) -> None: # noqa: C901 """Загрузка реальных данных из JSON.""" - for key in FILE_MODEL_MAP.items(): - file_name = key[0] + ".json" - if "main_" in key[0]: + with connection.cursor() as cursor: + for table_name in FILE_MODEL_MAP.keys(): try: - clear_data_db(file_name) + cursor.execute(f"TRUNCATE TABLE {table_name} CASCADE") except Exception as e: - self.stdout.write( - self.style.ERROR( - f"Ошибка удаления данных {e} -> " f"{file_name}", + return self.stdout.write( + self.style.WARNING( + f"Не удалось очистить таблицу {table_name}: {str(e)}", # noqa: E501 ), ) + items = list(FILE_MODEL_MAP.items()) items.reverse() - for key in items: - file_name = key[0] + ".json" - if "main_" in key[0]: - try: - importing_real_data_db(FIXSTURES_DIR, file_name) - self.stdout.write( - self.style.SUCCESS( - f"Фикстуры с файла {file_name} вставлены " - "в таблицы!", - ), - ) - except Exception as e: - return self.stdout.write( - self.style.ERROR_OUTPUT( - f"Ошибка вставки данных {e} -> " f"{file_name}", - ), - ) - return None + for table_name, model_class in items: + file_path = FIXSTURES_DIR / f"{table_name}.json" + app_label, model_name = table_name.split("_", 1) + model = apps.get_model(app_label, model_name) + try: + with open(file_path, "r", encoding="utf-8") as file: + data = json.load(file) + + for item in data: + if table_name == "main_team": + team_data = { + "id": item["id"], + "name": item["name"], + "city_id": item["city_id"], + "discipline_name_id": item["discipline_name_id"], + "curator_id": 1, + } + instance = model(**team_data) + elif table_name == "main_player": + disciplines = self.get_disciplines() + diagnosis = None + if item.get("diagnosis_id"): + try: + diagnosis = Diagnosis.objects.get( + pk=item["diagnosis_id"], + ) + except Diagnosis.DoesNotExist: + print( + f"Диагноз с id {item.get('diagnosis_id')} отсутсвует.", # noqa: E501 + ) + player_data = { + "id": item["id"], + "surname": item["surname"], + "name": item["name"], + "patronymic": item["patronymic"], + "birthday": item["birthday"], + "gender": item["gender"], + "level_revision": item["level_revision"], + "position": item["position"], + "number": item["number"], + "is_captain": item["is_captain"], + "is_assistent": item["is_assistent"], + "identity_document": item["identity_document"], + "diagnosis": diagnosis, + "diagnosis_id": item["diagnosis_id"], + "discipline_name_id": disciplines[ + item["discipline_id"] + ]["discipline_name_id"], + "discipline_level_id": disciplines[ + item["discipline_id"] + ]["discipline_level_id"], + } + instance = model(**player_data) + else: + instance = model(**item) + instance.save() + + self.stdout.write( + self.style.SUCCESS( + f"Данные из {table_name}.json успешно загружены в модель {model_class}", # noqa: E501 + ), + ) + + except FileNotFoundError: + self.stdout.write( + self.style.WARNING(f"Файл {table_name}.json не найден"), + ) + except Exception as e: + self.stdout.write( + self.style.ERROR( + f"Ошибка при загрузке данных из {table_name}.json: {str(e)}", # noqa: E501 + ), + ) def handle(self, *args, **options): """Запись данных в БД.""" - parser = options.get("parser") fixtures = options.get("fixtures") if fixtures: self.load_real_data() - if parser: - self.load_data() + + def get_disciplines(self) -> dict: + """Получение диспциплин.""" + with open( + FIXSTURES_DIR / "main_discipline.json", + "r", + encoding="utf-8", + ) as file: + data = json.load(file) + disciplines = { + None: {"discipline_level_id": None, "discipline_name_id": None}, + } + for item in data: + disciplines[item["id"]] = { + "discipline_level_id": item["discipline_level_id"], + "discipline_name_id": item["discipline_name_id"], + } + return disciplines diff --git a/adaptive_hockey_federation/parser/__init__.py b/adaptive_hockey_federation/parser/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/adaptive_hockey_federation/parser/docx_parser.py b/adaptive_hockey_federation/parser/docx_parser.py deleted file mode 100644 index 24f583be..00000000 --- a/adaptive_hockey_federation/parser/docx_parser.py +++ /dev/null @@ -1,517 +0,0 @@ -import re -from datetime import date -from typing import Optional - -import docx # type: ignore - -from adaptive_hockey_federation.parser.user_card import BaseUserInfo - -NAME = "[И|и][М|м][Я|я]|Ф.И.О." -SURNAME = "[Ф|ф][А|а][М|м][И|и][Л|л][И|и][Я|я]|Ф.И.О." -PATRONYMIC = "[О|о][Т|т]?[Ч|ч][Е|е][С|с][Т|т][В|в][О|о]|Ф.И.О." -DATE_OF_BIRTH = "[Д|д][А|а][Т|т][А|а] [Р|р][О|о].+" -TEAM = "[К|к][О|о][М|м][А|а][Н|н][Д|д][А|а]" -PLAYER_NUMBER = "[И|и][Г|г][Р|р][О|о][В|в][О|о][Й|й]" -POSITION = "[П|п][О|о][З|з][И|и][Ц|ц][И|и][Я|я]|Должность" -NUMERIC_STATUS = "[Ч|ч].+[С|с][Т|т].+" -PLAYER_CLASS = "[К|к][Л|л][А|а][С|с][С|с]" -PASSPORT = "[П|а][С|с][П|п][О|о][Р|р][Т|т]" -ASSISTENT = ["А", "а", "(А)", "(а)", "Ассистент", "ассистент"] -CAPTAIN = ["К", "к", "(К)", "(к)", "Капитан", "капитан"] -DISCIPLINE_LEVEL = "без ограничений" - - -def read_file_columns(file: docx) -> list[docx]: - """Функция находит таблицы в файле и возвращает список объектов - docx с данными каждого столбца. - """ - return [ - column for table in file.tables for index, column in enumerate(table.columns) - ] - - -def read_file_text(file: docx) -> list[str]: - """Функция находит текстовые данные в файле и возвращает список объектов - docx с найденными данными. - """ - return [run.text for paragraph in file.paragraphs for run in paragraph.runs] - - -def get_counter_for_columns_parser(columns: list[docx]) -> int: - count = 0 - for column in columns: - for index, cell in enumerate(column.cells): - if re.search(r"п/п", cell.text): - for cell in column.cells[index + 1 :]: - if cell.text and len(cell.text) < 4: - count += 1 - else: - break - else: - if count > 0: - break - return count - - -def columns_parser( - columns: list[docx], - regular_expression: str, -) -> list[Optional[str]]: - """Функция находит столбец по названию и списком выводит содержимое - каждой ячейки этого столбца. - """ - output = [ - text if text else None - for column in columns - if re.search(regular_expression, list(cell.text for cell in column.cells)[0]) - for text in list(cell.text for cell in column.cells)[1:] - ] - if not output: - count = get_counter_for_columns_parser(columns) - for column in columns: - for index, cell in enumerate(column.cells): - if re.search(regular_expression, cell.text): - for cell in column.cells[index + 1 : index + 1 + count]: - output.append(cell.text) - return output - - -def find_names(columns: list[docx], regular_expression: str) -> list[str]: - """Функция парсит в искомом столбце имена. Опирается на шаблон ФИО - (имя идет после фамилии на втором месте). - """ - names_list = columns_parser(columns, regular_expression) - return [name.split()[1].rstrip() for name in names_list if name] - - -def find_surnames(columns: list[docx], regular_expression: str) -> list[str]: - """Функция парсит в искомом столбце фамилии. Опирается на шаблон ФИО - (фамилия идет на первом месте). - """ - surnames_list = columns_parser(columns, regular_expression) - return [surname.split()[0].rstrip() for surname in surnames_list if surname] - - -def find_patronymics( - columns: list[docx], - regular_expression: str, -) -> list[str]: - """Функция парсит в искомом столбце отчества. Опирается на шаблон ФИО - (отчество идет на последнем месте). - """ - patronymics_list = columns_parser(columns, regular_expression) - return [ - ( - patronymic.replace("/", " ").split()[2].rstrip().rstrip(",") - if patronymic and len(patronymic.split()) > 2 - else "Отчество отсутствует" - ) - for patronymic in patronymics_list - ] - - -def find_dates_of_birth( - columns: list[docx], - regular_expression: str, -) -> list[date]: - """Функция парсит в искомом столбце дату рождения - и опирается на шаблон дд.мм.гггг. - """ - dates_of_birth_list = columns_parser(columns, regular_expression) - dates_of_birth_list_clear = [] - for date_of_birth in dates_of_birth_list: - if date_of_birth: - try: - for day, month, year in [re.sub(r"\D", " ", date_of_birth).split()]: - if len(year) == 2: - if int(year) > 23: - year = "19" + year - else: - year = "20" + year - dates_of_birth_list_clear.append( - date(int(year), int(month), int(day)) - ) - except ValueError or IndexError: - dates_of_birth_list_clear.append(date(1900, 1, 1)) - else: - dates_of_birth_list_clear.append(date(1900, 1, 1)) - - return dates_of_birth_list_clear - - -def find_team( - text: list[str], - columns: list[docx], - regular_expression: str, -) -> str: - """Функция парсит название команды.""" - text_clear = " ".join(text) - text_clear = re.sub( - r"\W+|_+|ХК|СХК|ДЮСХК|Хоккейный клуб|по незрячему хоккею" - "|по специальному хоккею|Спец хоккей|по специальному|по следж-хоккею", - " ", - text_clear, - ).split() - try: - return [ - ( - "Молния Прикамья" - if text_clear[index + 2] == "Прикамья" - else ( - "Ак Барс" - if text_clear[index + 1] == "Ак" - else ( - "Снежные Барсы" - if text_clear[index + 1] == "Снежные" - else ( - "Хоккей Для Детей" - if text_clear[index + 1] == "Хоккей" - else ( - "Дети-Икс" - if text_clear[index + 1] == "Дети" - else ( - "СКА-Стрела" - if text_clear[index + 1] == "СКА" - else ( - "Сборная Новосибирской области" - if text_clear[index + 2] == "Новосибирской" - else ( - "Атал" - if text_clear[index + 3] == "Атал" - else ( - "Крылья Мечты" - if text_clear[index + 2] == "мечты" - else ( - "Огни Магнитки" - if text_clear[index + 1] == "Огни" - else ( - "Энергия Жизни Краснодар" - if text_clear[index + 3] - == "Краснодар" - else ( - "Энергия Жизни Сочи" - if text_clear[index + 4] - == "Сочи" - else ( - "Динамо-Москва" - if text_clear[index + 1] - == "Динамо" - else ( - "Крылья Советов" - if text_clear[ - index + 2 - ] - == "Советов" - else ( - "Красная Ракета" - if text_clear[ - index + 2 - ] - == "Ракета" - else ( - "Красная Молния" - if text_clear[ - index - + 2 - ] - == "молния" - else ( - "Сахалинские Львята" - if text_clear[ - index - + 1 - ] - == "Сахалинские" - else ( - "Мамонтята Югры" - if text_clear[ - index - + 1 - ] - == "Мамонтята" - else ( - "Уральские Волки" - if text_clear[ - index - + 1 - ] - == "Уральские" - else ( - "Нет названия команды" - if text_clear[ - index - + 1 - ] - == "Всего" - else text_clear[ - index - + 1 - ].capitalize() - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - for index, txt in enumerate(text_clear) - if re.search(regular_expression, txt) - ][0] - except IndexError: - for column in columns: - for cell in column.cells: - if re.search(regular_expression, cell.text): - txt = re.sub(r"\W", " ", cell.text) - return txt.split()[1].capitalize() - - return "Название команды не найдено" - - -def find_players_number( - columns: list[docx], - regular_expression: str, -) -> list[int]: - """Функция парсит в искомом столбце номер игрока.""" - players_number_list = columns_parser(columns, regular_expression) - players_number_list_clear = [] - for player_number in players_number_list: - if player_number: - try: - players_number_list_clear.append( - int(re.sub(r"\D", "", player_number)[:2]) - ) - except ValueError: - players_number_list_clear.append(0) - else: - players_number_list_clear.append(0) - - return players_number_list_clear - - -def find_positions(columns: list[docx], regular_expression: str) -> list[str]: - """Функция парсит в искомом столбце позицию игрока на поле.""" - positions_list = columns_parser(columns, regular_expression) - return [ - ( - "нападающий" - if re.search( - r"^н|^Н|^H|^Нп|^нл|^нп|^цн|^лн|^Нап|^№|^А,|^К,", position.lstrip() - ) - else ( - "защитник" - if re.search(r"^з|^З|^Зщ|^Защ", position.lstrip()) - else ( - "вратарь" - if re.search(r"^Вр|^В|^вр", position.lstrip()) - else ( - "Позиция записана неверно" - if not re.sub(r"\n|\(.+|\d", "", position) - else re.sub(r"\n|\(.+|\d|Капитан", "", position) - .lower() - .rstrip() - .replace(",", "") - .lstrip() - ) - ) - ) - ) - for position in positions_list - if position - ] - - -def find_numeric_statuses(file: docx) -> list[list[str]]: - numeric_statuses_list = [] - for table in file.tables: - for row in table.rows: - txt = row.cells[1].text.title() - txt = re.sub(r"\W|Коляс.+|Здоровый", " ", txt) - if len(txt.split()) <= 4: - try: - numeric_status = row.cells[4].text - numeric_status = re.sub(r"\D", "", numeric_status) - if numeric_status: - if len(txt.split()) == 2: - txt += " Отчество отсутствует" - numeric_statuses_list.append(txt.split()[:3] + [numeric_status]) - except IndexError: - pass - - return numeric_statuses_list - - -def find_passport(columns: list[docx], regular_expression: str) -> list[str]: - """Функция парсит в искомом столбце ПД.""" - identity_list = columns_parser(columns, regular_expression) - return [ - " ".join(identity_data.split()) - for identity_data in identity_list - if identity_data - ] - - -def find_players_is_captain(columns: list[docx], regular_expression: str) -> list[bool]: - """Функция парсит в искомом столбце капитанов.""" - is_captain_list = [] - for is_captain in columns_parser(columns, regular_expression): - for i in CAPTAIN: - if is_captain and i in is_captain.strip(): - try: - is_captain_list.append(True) - except ValueError: - is_captain_list.append(False) - else: - is_captain_list.append(False) - return is_captain_list - - -def find_players_is_assistant( - columns: list[docx], - regular_expression: str, -) -> list[bool]: - """Функция парсит в искомом столбце асситсента.""" - is_assistant_list = [] - for is_assistant in columns_parser(columns, regular_expression): - for i in ASSISTENT: - if is_assistant and i in is_assistant.strip(): - try: - is_assistant_list.append(True) - except ValueError: - is_assistant_list.append(False) - else: - is_assistant_list.append(False) - return is_assistant_list - - -def find_discipline_level( - columns: list[docx], - regular_expression: str, -) -> list[str]: - """Функция парсит в искомом столбце класс/статус.""" - discipline_level_list = [] - for discipline_level in columns_parser(columns, regular_expression): - if discipline_level: - try: - discipline_level = discipline_level.replace("Класс ", "").replace( - "класс ", "" - ) - discipline_level = re.sub( - r"без ограничений|Не имеет ограничений по здоровью" - "|Без ограничений по здоровью" - "|4Без ограничений" - "|Без ограничений\n4|без ограничений по здоровью 2" - "|игрок без ограничений по здоровью" - "|(без ограничений по здоровью) 3" - "|без ограничений 2|Не имеет ограничений по здоровью", - "б\\к", - discipline_level, - ) - if discipline_level != "б\\к": - discipline_level = ( - discipline_level.replace("А", "A") - .replace("С", "C") - .replace("Б", "B") - .replace("б", "B") - ) - discipline_level_list.append(discipline_level) - except ValueError: - discipline_level_list.append("") - else: - discipline_level_list.append("") - return discipline_level_list - - -def numeric_status_check( - name: str, - surname: str, - patronymics: str, - statuses: list[list[str]], -) -> Optional[int]: - for status in statuses: - if surname == status[0]: - if name == status[1]: - if patronymics.split()[0] == status[2]: - return int(status[3]) - - return None - - -def length_list(name: list, len_name: int) -> None: - if len(name) != len_name: - for _ in range(len_name - len(name)): - name.append(None) - return None - - -def docx_parser(path: str, numeric_statuses: list[list[str]]) -> list[BaseUserInfo]: - """Функция собирает все данные об игроке - и передает их в dataclass. - """ - file = docx.Document(path) - columns_from_file = read_file_columns(file) - text_from_file = read_file_text(file) - names = find_names(columns_from_file, NAME) - surnames = find_surnames(columns_from_file, SURNAME) - patronymics = find_patronymics(columns_from_file, PATRONYMIC) - dates_of_birth = find_dates_of_birth( - columns_from_file, - DATE_OF_BIRTH, - ) - team = find_team(text_from_file, columns_from_file, TEAM) - players_number = find_players_number(columns_from_file, PLAYER_NUMBER) - positions = find_positions(columns_from_file, POSITION) - passport = find_passport(columns_from_file, PASSPORT) - is_assistents = find_players_is_assistant(columns_from_file, PLAYER_NUMBER) - is_assistents_alt = find_players_is_assistant(columns_from_file, POSITION) - is_captains = find_players_is_captain(columns_from_file, PLAYER_NUMBER) - is_captains_alt = find_players_is_captain(columns_from_file, POSITION) - classification = find_discipline_level(columns_from_file, DISCIPLINE_LEVEL) - - length_list(players_number, len(names)) - length_list(is_assistents, len(names)) - length_list(is_captains, len(names)) - length_list(classification, len(names)) - length_list(passport, len(names)) - length_list(positions, len(names)) - - return [ - BaseUserInfo( - name=names[index], - surname=surnames[index], - date_of_birth=dates_of_birth[index], - team=team, - player_number=players_number[index], - position=positions[index], - numeric_status=numeric_status_check( - names[index], - surnames[index], - patronymics[index], - numeric_statuses, - ), - patronymic=patronymics[index], - passport=passport[index], - is_assistant=( - is_assistents[index] - if is_assistents[index] - else is_assistents_alt[index] - ), - is_captain=( - is_captains[index] if is_captains[index] else is_captains_alt[index] - ), - classification=classification[index], - ).__dict__ - for index in range(len(names)) - ] diff --git a/adaptive_hockey_federation/parser/exception.py b/adaptive_hockey_federation/parser/exception.py deleted file mode 100644 index c55dfaf8..00000000 --- a/adaptive_hockey_federation/parser/exception.py +++ /dev/null @@ -1,2 +0,0 @@ -class ExceptionForFlake8(Exception): - pass diff --git a/adaptive_hockey_federation/parser/importing_db.py b/adaptive_hockey_federation/parser/importing_db.py deleted file mode 100644 index 1effb0c3..00000000 --- a/adaptive_hockey_federation/parser/importing_db.py +++ /dev/null @@ -1,261 +0,0 @@ -import json -import subprocess - -from django.db import connection, transaction -from main import models -from main.models import (DisciplineLevel, DisciplineName, Player, StaffMember, - StaffTeamMember, Team) - -from adaptive_hockey_federation.core.config.dev_settings import ( - FILE_MODEL_MAP, RESOURSES_ROOT) -from adaptive_hockey_federation.parser.user_card import BaseUserInfo -from main import models -from main.models import (Diagnosis, DisciplineLevel, DisciplineName, Player, StaffMember, - StaffTeamMember, Team, Nosology) - -MODELS_ONE_FIELD_NAME = ["main_city", "main_disciplinename", "main_nosology"] - -PLAYER_POSITIONS = [ - "нападающий", - "поплавок", - "вратарь", - "защитник", - "Позиция записана неверно", -] -STAFF_POSITIONS = [ - "тренер", - "координатор", - "пушер", -] - - -def parse_file(file_path: str) -> list[BaseUserInfo]: - with open(file_path, "r", encoding="utf-8") as file: - data = json.load(file) - return data - - -def get_discipline_name(item_name: str): - try: - discipline_name = DisciplineName.objects.get(name=item_name) - except DisciplineName.DoesNotExist: - discipline_name = None - return discipline_name - - -def get_discipline_level(item_name: str): - try: - discipline_level = DisciplineLevel.objects.get(name=item_name) - except DisciplineLevel.DoesNotExist: - discipline_level = None - return discipline_level - - -def create_staff_member(item): - try: - try: - staff_member = StaffMember( - surname=item["surname"], - name=item["name"], - patronymic=item["patronymic"], - ) - staff_member.save() - - staff_team_member = StaffTeamMember( - staff_position=item["position"], - staff_member_id=staff_member.id, - notes=item["date_of_birth"].replace(" 00:00:00", ""), - ) - - staff_team_member.save() - team = Team.objects.get(name=item["team"]) - staff_team_member_id = StaffTeamMember.objects.get( - staff_position__contains="тренер", pk=staff_team_member.id - ) - if team.staff_team_member_id != staff_team_member_id: - team.staff_team_member_id = staff_team_member_id - team.save() - return team - except StaffTeamMember.DoesNotExist: - team = None - except Exception as e: - print(f"Ошибка вставки данных {e} -> {item}") - - -def create_players(item, discipline_name) -> None: - try: - player_model = Player( - surname=item["surname"], - name=item["name"], - patronymic=item["patronymic"], - birthday=item["date_of_birth"].replace(" 00:00:00", ""), - gender="", - level_revision=item["revision"], - position=item["position"], - number=item["player_number"], - is_captain=item["is_captain"], - is_assistent=item["is_assistant"], - identity_document=item["passport"], - discipline_name=discipline_name, - ) - player_model.save() - teams = Team.objects.get(name=item["team"]) - player_model.team.add(teams) - - except Exception as e: - print(f"Ошибка вставки данных {e} -> {item}") - - -# flake8: noqa: C901 -def importing_parser_data_db(FIXSTURES_FILE: str) -> None: - subprocess.getoutput(f"poetry run parser -r -p {RESOURSES_ROOT}") - data = parse_file(FIXSTURES_FILE) - for item in data: - for key in item: - if item[key] is None and key != "player_number": - item[key] = "" - if item[key] is None and key == "player_number": - item[key] = 0 - for i in PLAYER_POSITIONS: - if i in item["position"]: - create_players(item, get_discipline_name(item["classification"])) - for i in STAFF_POSITIONS: - if i in item["position"]: - create_staff_member(item) - - -def clear_data_db(file_name: str) -> None: - key = file_name.replace(".json", "") - models_name = getattr(models, FILE_MODEL_MAP[key]) - models_name.objects.all().delete() - cursor = connection.cursor() - cursor.execute( - f"SELECT setval(pg_get_serial_sequence('{key}', 'id')," - f"coalesce(max(id), 1), max(id) IS NOT null)" - f"FROM {key};" - ) - - -def parse_disciplines(FIXSTURES_DIR) -> dict: - with open(FIXSTURES_DIR / "main_discipline.json", "r", encoding="utf-8") as file: - data = json.load(file) - disciplines = {None: {"discipline_level_id": None, "discipline_name_id": None}} - for item in data: - disciplines[item["id"]] = { - "discipline_level_id": item["discipline_level_id"], - "discipline_name_id": item["discipline_name_id"], - } - return disciplines - - -def importing_real_data_db(FIXSTURES_DIR, file_name: str) -> None: - with open(FIXSTURES_DIR / file_name, "r", encoding="utf-8") as file: - data = json.load(file) - key = file_name.replace(".json", "") - models_name = getattr(models, FILE_MODEL_MAP[key]) - if key == "main_player": - disciplines = parse_disciplines(FIXSTURES_DIR) - max_id = 0 - for item in data: - max_id = max(max_id, item["id"]) - try: - if key in MODELS_ONE_FIELD_NAME: - model_ins = models_name( - id=item["id"], - name=item["name"] - ) - model_ins.save() - if key == "main_staffmember": - model_ins = models_name( - id=item["id"], - surname=item["surname"], - name=item["name"], - patronymic=item["patronymic"], - ) - model_ins.save() - if key == "main_disciplinelevel": - model_ins = models_name( - id=item["id"], - name=item["name"], - discipline_name_id=item["discipline_name_id"], - ) - model_ins.save() - if key == "main_staffteammember": - model_ins = models_name( - id=item["id"], - staff_position=item["staff_position"], - qualification=item["qualification"], - notes=item["notes"], - staff_member_id=item["staff_member_id"], - ) - model_ins.save() - - if key == "main_diagnosis": - nosology = None - if item.get("nosology_id"): - try: - nosology = Nosology.objects.get(pk=item["nosology_id"]) - except Nosology.DoesNotExist: - print(f"Нозологии с id {item['nosology_id']} не существует.") - model_ins = models_name( - id=item["id"], - name=item["name"], - nosology=nosology, - ) - - model_ins.save() - if key == "main_team": - model_ins = models_name( - id=item["id"], - name=item["name"], - city_id=item["city_id"], - discipline_name_id=item["discipline_name_id"], - curator_id=1, - ) - model_ins.save() - if item["staff_team_member_id"]: - staff_team_member = StaffTeamMember.objects.get( - pk=item["staff_team_member_id"] - ) - team = Team.objects.get(pk=item["id"]) - staff_team_member.team.add(team) - if key == "main_player": - diagnosis = None - if item.get("diagnosis_id"): - try: - diagnosis = Diagnosis.objects.get(pk=item["diagnosis_id"]) - except Diagnosis.DoesNotExist: - print(f"Диагноз с id {item.get('diagnosis_id')} отсутсвует.") - model_ins = models_name( - id=item["id"], - surname=item["surname"], - name=item["name"], - patronymic=item["patronymic"], - birthday=item["birthday"], - gender=item["gender"], - level_revision=item["level_revision"], - position=item["position"], - number=item["number"], - is_captain=item["is_captain"], - is_assistent=item["is_assistent"], - identity_document=item["identity_document"], - diagnosis=diagnosis, - diagnosis_id=item["diagnosis_id"], - discipline_name_id=disciplines[item["discipline_id"]][ - "discipline_name_id" - ], - discipline_level_id=disciplines[item["discipline_id"]][ - "discipline_level_id" - ], - ) - model_ins.save() - if key == "main_player_team": - player = Player.objects.get(pk=item["player_id"]) - team = Team.objects.get(pk=item["team_id"]) - player.team.add(team) - - except Exception as e: - print(f"Ошибка вставки данных {e} -> {item}") - cursor = connection.cursor() - cursor = cursor.execute(f"ALTER SEQUENCE {key}_id_seq RESTART WITH {max_id+1};") - transaction.commit() diff --git a/adaptive_hockey_federation/parser/parser.py b/adaptive_hockey_federation/parser/parser.py deleted file mode 100644 index 5da6c5e3..00000000 --- a/adaptive_hockey_federation/parser/parser.py +++ /dev/null @@ -1,108 +0,0 @@ -import json -import os -from pprint import pprint - -import click -import docx # type: ignore - -from adaptive_hockey_federation.core.config.dev_settings import ( - FIXSTURES_DIR, FIXSTURES_FILE) -from adaptive_hockey_federation.parser.docx_parser import ( - docx_parser, find_numeric_statuses) -from adaptive_hockey_federation.parser.xlsx_parser import xlsx_parser - -NUMERIC_STATUSES = "Числовые статусы следж-хоккей 02.10.203.docx" -FILES_BLACK_LIST = [ - "На мандатную комиссию", - "Именная заявка следж-хоккей Энергия Жизни Сочи", - "ФАХ Сияжар Взрослые", - "Числовые статусы следж-хоккей 02.10.203", -] -FILES_EXTENSIONS = [ - ".docx", - ".xlsx", -] -NUMERIC_STATUSES_FILE_ERROR = ( - "Не могу найти {}. Без него не" - " получиться загрузить именные заявки." - " Файл должен находиться в директории с" - " файлами для парсинга" -) - - -@click.command() -@click.option( - "-p", - "--path", - required=True, - help="Путь до папки с файлами для парсинга", -) -@click.option( - "-r", - "--result", - is_flag=True, - help="Вывод в консоль извлеченных данных и статистики", -) -def parsing_file(path: str, result: bool) -> None: - """Функция запускает парсинг файлов в рамках проекта. - Запуск через командную строку: - 'python parser.py -p(--path) путь_до_папки_с_файлами' - Вызов справки 'python parser.py -h(--help)' - """ - results_list = [] - files, numeric_statuses_file = get_all_files(path) - if numeric_statuses_file is None: - click.echo(NUMERIC_STATUSES_FILE_ERROR.format(NUMERIC_STATUSES)) - return - numeric_statuses = find_numeric_statuses(docx.Document(numeric_statuses_file)) - click.echo(f"Найдено {len(files)} файлов.") - for file in files: - if file.endswith("docx"): - results_list.extend(docx_parser(file, numeric_statuses)) - else: - results_list.extend(xlsx_parser(file)) # type: ignore - if result: - for data in results_list: - pprint(data) - - if not os.path.exists(FIXSTURES_DIR): - os.makedirs(FIXSTURES_DIR) - json.dump( - results_list, - open(FIXSTURES_FILE, "w", encoding="utf8"), - ensure_ascii=False, - indent=4, - default=str, - ) - - results_list = list(results_list) - - click.echo(f"Успешно обработано {len(files)} файлов.") - click.echo(f"Извлечено {len(results_list)} уникальных записей") - - -def get_all_files(path: str) -> tuple[list[str], str | None]: - """Функция извлекает из папки, в том числе вложенных, - список всех файлов и отдельно путь до файла с числовыми статусами. - Извлекаются только файлы с расширениями указанными в константе - FILES_EXTENSIONS (по умолчанию docx, xlsx) и не извлекает файлы, название - которых без расширения указано в списке FILES_BLACK_LIST. - """ - files = [] - numeric_statuses_filepath = None - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - if filename == NUMERIC_STATUSES: - numeric_statuses_filepath = os.path.join(dirpath, filename) - file, extension = os.path.splitext(filename) - if ( - not file.startswith("~") - and extension in FILES_EXTENSIONS - and file not in FILES_BLACK_LIST - ): - files.append(os.path.join(dirpath, filename)) - return files, numeric_statuses_filepath - - -if __name__ == "__main__": - parsing_file() diff --git a/adaptive_hockey_federation/parser/user_card.py b/adaptive_hockey_federation/parser/user_card.py deleted file mode 100644 index ad3d29c9..00000000 --- a/adaptive_hockey_federation/parser/user_card.py +++ /dev/null @@ -1,46 +0,0 @@ -from dataclasses import dataclass -from datetime import date -from typing import Union - - -@dataclass -class BaseUserInfo: - """Основной класс с обязательными полями.""" - - name: Union[str, None] - surname: Union[str, None] - date_of_birth: Union[date, None] - team: Union[str, None] - player_number: Union[int, None] - position: Union[str, None] - numeric_status: Union[int, None] - patronymic: Union[str, None] = None - birth_certificate: Union[str, None] = None - passport: Union[str, None] = None - classification: Union[str, None] = None - revision: Union[str, None] = None - is_assistant: bool = False - is_captain: bool = False - - def __eq__(self, other): - return all(getattr(self, attr) == getattr(other, attr) for attr in vars(self)) - - def __hash__(self): - return hash( - ( - self.name, - self.surname, - self.date_of_birth, - self.team, - self.player_number, - self.position, - self.numeric_status, - self.patronymic, - self.birth_certificate, - self.passport, - self.classification, - self.revision, - self.is_assistant, - self.is_captain, - ) - ) diff --git a/adaptive_hockey_federation/parser/xlsx_parser.py b/adaptive_hockey_federation/parser/xlsx_parser.py deleted file mode 100644 index 51046f58..00000000 --- a/adaptive_hockey_federation/parser/xlsx_parser.py +++ /dev/null @@ -1,31 +0,0 @@ -import openpyxl - -from adaptive_hockey_federation.parser.user_card import BaseUserInfo - - -def xlsx_parser(path: str) -> list[BaseUserInfo]: - """Функция парсит xlsx файлы и возвращает - игроков в виде dataclass ExcelData. - """ - players = [] - sheet_data = [] - workbook = openpyxl.load_workbook(path) - sheet = workbook.active - header = [cell.value for cell in sheet[1]] # type: ignore - for row in sheet.iter_rows(min_row=2, values_only=True): # type: ignore - sheet_data.append(dict(zip(header, row))) - for data in sheet_data: - if data.get("ФИО игрока") is not None: - player = BaseUserInfo( - team=data.get("Команда"), - name=data.get("ФИО игрока").split()[0], # type: ignore - surname=data.get("ФИО игрока").split()[1], # type: ignore - date_of_birth=data.get("Дата рождения"), - player_number=data.get("Номер игрока"), - position=data.get("Позиция"), - classification=data.get("Класс"), - revision=data.get("Пересмотр (начало сезона)"), - numeric_status=None, - ) - players.append(player.__dict__) - return players diff --git a/pyproject.toml b/pyproject.toml index 2a266ac1..ac25ba25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,6 @@ build-backend = "poetry.core.masonry.api" target-version = "py311" exclude = [ "config", - "adaptive_hockey_federation/parser/*", "*migrations/*", ".bzr", ".direnv", @@ -142,9 +141,6 @@ inline-quotes = "double" [tool.django-stubs] django_settings_module = "adaptive_hockey_federation.core.config.dev_settings" -[tool.poetry.scripts] -parser = "adaptive_hockey_federation.parser.parser:parsing_file" - [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "adaptive_hockey_federation.core.config.test_settings" python_files = ["*_test.py"] diff --git a/requirements/develop.txt b/requirements/develop.txt index bb41838a..f83ebd67 100644 --- a/requirements/develop.txt +++ b/requirements/develop.txt @@ -1,18 +1,9 @@ -amqp==5.2.0 ; python_version >= "3.11" and python_version < "4.0" annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0" anyio==4.4.0 ; python_version >= "3.11" and python_version < "4.0" asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" -async-timeout==4.0.3 ; python_version >= "3.11" and python_full_version < "3.11.3" attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" -billiard==4.2.0 ; python_version >= "3.11" and python_version < "4.0" -celery-singleton==0.3.1 ; python_version >= "3.11" and python_version < "4.0" -celery==5.4.0 ; python_version >= "3.11" and python_version < "4.0" -celery[redis]==5.4.0 ; python_version >= "3.11" and python_version < "4.0" certifi==2024.6.2 ; python_version >= "3.11" and python_version < "4.0" cfgv==3.4.0 ; python_version >= "3.11" and python_version < "4.0" -click-didyoumean==0.3.1 ; python_version >= "3.11" and python_version < "4.0" -click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" -click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" @@ -31,19 +22,16 @@ faker==26.0.0 ; python_version >= "3.11" and python_version < "4.0" fastapi-cli[standard]==0.0.5 ; python_version >= "3.11" and python_version < "4.0" fastapi[standard]==0.112.0 ; python_version >= "3.11" and python_version < "4.0" filelock==3.15.4 ; python_version >= "3.11" and python_version < "4.0" -flower==2.0.1 ; python_version >= "3.11" and python_version < "4.0" gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0" h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0" httptools==0.6.1 ; python_version >= "3.11" and python_version < "4.0" httpx==0.27.0 ; python_version >= "3.11" and python_version < "4.0" -humanize==4.9.0 ; python_version >= "3.11" and python_version < "4.0" identify==2.5.36 ; python_version >= "3.11" and python_version < "4.0" idna==3.7 ; python_version >= "3.11" and python_version < "4.0" inflection==0.5.1 ; python_version >= "3.11" and python_version < "4.0" iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0" jinja2==3.1.4 ; python_version >= "3.11" and python_version < "4.0" -kombu==5.3.7 ; python_version >= "3.11" and python_version < "4.0" markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0" markupsafe==2.1.5 ; python_version >= "3.11" and python_version < "4.0" mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0" @@ -51,7 +39,7 @@ mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0" mypy==1.10.1 ; python_version >= "3.11" and python_version < "4.0" nodeenv==1.9.1 ; python_version >= "3.11" and python_version < "4.0" numpy==2.0.0 ; python_version >= "3.11" and python_version < "4.0" -opencv-python-headless==4.10.0.84 ; python_version >= "3.11" and python_version < "4.0" +opencv-python==4.10.0.84 ; python_version >= "3.11" and python_version < "4.0" openpyxl-stubs==0.1.25 ; python_version >= "3.11" and python_version < "4.0" openpyxl==3.1.5 ; python_version >= "3.11" and python_version < "4.0" packaging==24.1 ; python_version >= "3.11" and python_version < "4.0" @@ -60,8 +48,6 @@ pillow==10.3.0 ; python_version >= "3.11" and python_version < "4.0" platformdirs==4.2.2 ; python_version >= "3.11" and python_version < "4.0" pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0" pre-commit==3.5.0 ; python_version >= "3.11" and python_version < "4.0" -prometheus-client==0.20.0 ; python_version >= "3.11" and python_version < "4.0" -prompt-toolkit==3.0.47 ; python_version >= "3.11" and python_version < "4.0" psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0" pydantic-core==2.20.1 ; python_version >= "3.11" and python_version < "4.0" pydantic==2.8.2 ; python_version >= "3.11" and python_version < "4.0" @@ -74,9 +60,7 @@ python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0" pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" -redis==5.0.7 ; python_version >= "3.11" and python_version < "4.0" rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0" -requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" ruff==0.4.10 ; python_version >= "3.11" and python_version < "4.0" setuptools==70.1.1 ; python_version >= "3.11" and python_version < "4.0" shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0" @@ -84,20 +68,17 @@ six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0" -tornado==6.4.1 ; python_version >= "3.11" and python_version < "4.0" typer==0.12.3 ; python_version >= "3.11" and python_version < "4.0" types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" types-pillow==10.2.0.20240520 ; python_version >= "3.11" and python_version < "4.0" types-python-dateutil==2.9.0.20240316 ; python_version >= "3.11" and python_version < "4.0" typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0" -tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" +tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "win32" uritemplate==4.1.1 ; python_version >= "3.11" and python_version < "4.0" uvicorn[standard]==0.30.5 ; python_version >= "3.11" and python_version < "4.0" uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0" -vine==5.1.0 ; python_version >= "3.11" and python_version < "4.0" virtualenv==20.26.3 ; python_version >= "3.11" and python_version < "4.0" watchfiles==0.23.0 ; python_version >= "3.11" and python_version < "4.0" -wcwidth==0.2.13 ; python_version >= "3.11" and python_version < "4.0" websockets==12.0 ; python_version >= "3.11" and python_version < "4.0" wrapt==1.16.0 ; python_version >= "3.11" and python_version < "4.0" yadisk==3.1.0 ; python_version >= "3.11" and python_version < "4.0" diff --git a/requirements/production.txt b/requirements/production.txt index 1f8987ff..7f6f39ca 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -1,14 +1,5 @@ -amqp==5.2.0 ; python_version >= "3.11" and python_version < "4.0" asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" -async-timeout==4.0.3 ; python_version >= "3.11" and python_full_version < "3.11.3" attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" -billiard==4.2.0 ; python_version >= "3.11" and python_version < "4.0" -celery-singleton==0.3.1 ; python_version >= "3.11" and python_version < "4.0" -celery==5.4.0 ; python_version >= "3.11" and python_version < "4.0" -celery[redis]==5.4.0 ; python_version >= "3.11" and python_version < "4.0" -click-didyoumean==0.3.1 ; python_version >= "3.11" and python_version < "4.0" -click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" -click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" @@ -21,7 +12,6 @@ et-xmlfile==1.1.0 ; python_version >= "3.11" and python_version < "4.0" gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0" inflection==0.5.1 ; python_version >= "3.11" and python_version < "4.0" iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0" -kombu==5.3.7 ; python_version >= "3.11" and python_version < "4.0" mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0" mypy==1.10.1 ; python_version >= "3.11" and python_version < "4.0" openpyxl-stubs==0.1.25 ; python_version >= "3.11" and python_version < "4.0" @@ -30,23 +20,17 @@ packaging==24.1 ; python_version >= "3.11" and python_version < "4.0" phonenumbers==8.13.39 ; python_version >= "3.11" and python_version < "4.0" pillow==10.3.0 ; python_version >= "3.11" and python_version < "4.0" pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0" -prompt-toolkit==3.0.47 ; python_version >= "3.11" and python_version < "4.0" psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0" pytest-django==4.8.0 ; python_version >= "3.11" and python_version < "4.0" pytest-subtests==0.12.1 ; python_version >= "3.11" and python_version < "4.0" pytest==8.2.2 ; python_version >= "3.11" and python_version < "4.0" -python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0" pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" -redis==5.0.7 ; python_version >= "3.11" and python_version < "4.0" -six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" types-python-dateutil==2.9.0.20240316 ; python_version >= "3.11" and python_version < "4.0" typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0" -tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" +tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "win32" uritemplate==4.1.1 ; python_version >= "3.11" and python_version < "4.0" -vine==5.1.0 ; python_version >= "3.11" and python_version < "4.0" -wcwidth==0.2.13 ; python_version >= "3.11" and python_version < "4.0" wrapt==1.16.0 ; python_version >= "3.11" and python_version < "4.0" yadisk==3.1.0 ; python_version >= "3.11" and python_version < "4.0" From ccaef8a49ae5a33c6a6d556ab0f0e2afcab92fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=93=D0=BE=D0=B2=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D1=81=D0=BA=D0=B8=D0=B9?= Date: Tue, 1 Oct 2024 20:16:33 +0500 Subject: [PATCH 124/126] fix by review --- .github/workflows/pytest.yaml | 1 - infra/prod/docker-compose.prod.yaml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 1beb24f4..f08deb26 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -4,7 +4,6 @@ on: push: branches: - dev - - feature/* jobs: pytest: diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index dc83a6b8..9e978a92 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -1,5 +1,5 @@ version: '3.8' -name: ahf_stage +name: ahf_prod services: db: From 7ef3429e9243b41cf5caaad64008cecdca1700fb Mon Sep 17 00:00:00 2001 From: Konstantin Raikhert <69113745+KonstantinRaikhert@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:23:44 +0300 Subject: [PATCH 125/126] new relize (#558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * Create/endpoint game (#442) * create endpoint_game * create endpoint_game * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * test cd * fix: remove games button for team agents (#445) * Bugfix/game creation form fixes (#441) * feat: add basic admin pages for new models * refactor: remove wrong constraint * bugfix: fix edit game form, now working but not completely * bugfix: fix edit game form, now working properly * perf: add more precise team filtering --------- Co-authored-by: Konstantin Raikhert * test cd * Add docker scripts in Makefile and add ds_server in gitignore (#447) * Add docker scripts in Makefile and add ds_server in gitignore * Add new make commands in help * Update README.md * Решен баг с заполением БД реальными данными (#451) * fix player_video_games views (#452) Co-authored-by: zaritskiyaa * fix stage * Feature/models id sync (#454) * refactor: merge migrations and create fresh initial * feat: create new PK field for models, sync objects' ids * fix: fix docstring error * change ER diagram --------- Co-authored-by: Konstantin Raikhert * Bugfix/template fixes (#458) * fix: remove unload for unloads * fix: fix player's teams display * Fix search display, implement searching games and add icons for edit/delete games (#461) * Fix search display, implement seatching games and add icons for edit/delete games * delete print --------- Co-authored-by: Konstantin Raikhert * fix: fix game creation via admin panel, also add basic form validation (#460) Co-authored-by: Konstantin Raikhert * Edit players_number (#453) * Edit players_number * Edit players_number * Ruff fix * Fix ruff v2 * Ruff fix v3 * Fix templates * delete unsupport file * ruff check --------- Co-authored-by: Konstantin Raikhert * Create/video recognition (#455) * Save changes in poetry.lock * code according to PEP8 * create endpoint video_recognition * fix video api * code according to PEP8 * code according to PEP8 * code according to PEP8 * code according to PEP8 * code according to PEP8 --------- Co-authored-by: Konstantin Raikhert * refactoring video_api views and swagger doc (#464) * refactoring video_api views and swagger doc * expand service/a_hockey_requests.py/send_request_to_video_processing_service. Assigned try name for env param * fix env * fix bug with init environ object --------- Co-authored-by: zaritskiyaa * add requirement * fix name mistake in env param (#465) Co-authored-by: zaritskiyaa * Bugfix/player number edit form (#470) * fix: fix template for player number edit, also fix errors display * fix: add view name * Feature/factories and bugfix in player (#472) * fix player * add factory game * rebild factory * fix factory gamecreate * fix factory gamecreate * fix ruff * fix warning timedata --------- Co-authored-by: Максим Портнов Co-authored-by: Алексей Сосов~ * fix game_team query (#477) Co-authored-by: zaritskiyaa * Add GameDataPlayer model for storing game JSON data (#481) * Add GameDataPlayer model for storing game JSON data * add jpg in ER docs --------- Co-authored-by: Konstantin Raikhert * connect celery with redis back/broker. create mock tasks (#484) Co-authored-by: zaritskiyaa Co-authored-by: Konstantin Raikhert * Add JSON player data factory (#483) * add mock functional for processing video (#486) * add mock functional for processing video * fix readme --------- Co-authored-by: zaritskiyaa * Bugfix/fix analytic table in uploads (#493) * fix_analytic_table_in_uploads * fix_analytics_table_uploads --------- Co-authored-by: Tom Bulmer * rework GameDataPlayer model, refactoring mock celery tasks/serializer (#489) Co-authored-by: zaritskiyaa * Add celery instruction * add celery instruction * fix migrations conflict * Add feature dynamic video button in player_views and templates player_id and video_games_button. And fixed the filter in the PlayerGamesVideo view by id instead of pk (#496) * Feature/readme fix (#500) * fix .gitignore and README * mv pytest.ini in root folder * Revert .python-version change * Bugfix/template fix game detail (#499) * Fix button game_detail button visual bug * Add .python-version into gitignore * move into the right center * Added the feature of an additional column name of the competition on the games page. Added data to the context in the view of the GamesListView (#497) * add make command for kson factory (#498) * test_status_service (#501) Co-authored-by: Tom Bulmer * Added diagram video workers in docs. (#508) Deleted commands for DS server from Makefile (image_video, start_video). Deleted obsoleted units about parsing files and API DS from README.md. * Update the game player model - update a player number unique constraint (#505) (#510) * Update game player number unique constraint (#505) * Fix code-style ruff issues * Fix code-style ruff issues (#505) * Update unique game player number constraint fields; update fields in create game player signal (#505) * Refactor requests to DS API, add YandexDist token to settings and .env.example (#511) * Tests remarks (#514) * Add show message for unload_player_game_video (#509) (#512) * feat: Add show message for unload_player_game_video. fix: Change ' to " in base_settings.py. * fix: Revert " to ' in base_settings.py. * fix: Add game name to message. * Add validation to check unique player number field in player update form, edit players numbers in game form. (#517) * Feature/download player video (#520) (#521) * feat: Add download player video if video link exists. feat: Add yadisk in poetry. * fix: Refactor code. * fix: Move player video dir to constants. fix: Change path video dir to path video file. * fix: Add type hinting. fix: Refactor code. * fix: Change varialble name from error to error_message. * add TODO --------- Co-authored-by: Konstantin Raikhert * Bugfix/visible form adding player in team (#518) (#523) * fix: Correct misprints in template. fix: Fix visible form for adding teams. fix: Save added or updated player's teams. * fix: Change PlayerForm for visible team form. * fix: Move mixin in mixins.py and ajax view in ajax.py. * Bugfix/change footer buttons color (#525) (#526) * fix: Add return button to pages: staff_id_team_edit, staff_id_team_create, edit_team_players_numbers, game_edit. * fix: Change pagination border to border-2. fix: Correct position of footer buttons. * fix: Change color of footer buttons. * Integrate Celery worker and flower run into Make run (Issue #515) (#527) * Integrate Celery worker and flower run into Make run * add celery connction in config django --------- Co-authored-by: Konstantin Raikhert * Feature/django message in game card (#519) (#522) * Add django messages to game detail page. * Add django messages to game detail page. * Fix issue #507 - fix stage (#530) * Fix issue #507 - fix dependencies * Fix issue #507 - optimze dependencies fix * Fix stage deployment error (#535) * Add celery tasks to download videos from Y.disk, to slice video with player moments. (#532) * Feature/Add DS mock server (#533) * feat: Add fastapi[standard] in development requirements. * feat: Add FastAPI ds mock server. * feat: Add ds-mock command to Makefile. * fix: Delete queues from Celery. * fix: Change directory service to Django app. * feat: Add DS mock server. * fix: Delete redundant logging. * fix: Add logging to a_hockey_requests. fix: Change raise error to message. * fix: Delete redundant files. * fix: Change print to logging. fix: Correct import sort. * fix: Correct docstring in apps.py. * fix: Update README.md. * fix: Add file with test response json. * add some TODO * fix: Move mock_ds_server in separate directory. * fix: Update Makefile. * feat: Add mock_ds_server in Celery autodiscovery tasks. * fix: Add logging and return error messages in send_game_video_to_process. * fix: Add logging and change exception catch in send_request_to_process_video. * fix: Move mock_ds_server in separate directory. * fix: Change GameFeatureSerializer to structure of DS answer. * fix: Add logging and small refactoring. * fix: Add type hints in GameFeatureSerializer. * fix: Update Makefile * feat: Return Celery queues * fix: Correct task arguments * fix: Change response of mock DS server Small refactor code. * fix: Change logging level * fix: Change bulk_create_gamedataplayer_objects To structure response mock DS server * Revert "Merge branch 'dev' into feature/ds-mock-server" This reverts commit 879a5adc1117028ff1f7c4be7ec9670b6c245d63, reversing changes made to 2cd61257097a9b50b12b4784a805fa9458c11ad3. * fix: Delete unnecessary code. * fix: Add try..catch exceptions. * fix: Change game name to game id. * fix: Old data of players rewrite, if exist. * feat: Unlink mock DS server from the Django server. * fix: Revert RequestException. * feat: Add TODO for send mail. * fix: Delete unused import. * feat: Add send mail of end video process. --------- Co-authored-by: Konstantin Raikhert * fix bug (#538) Co-authored-by: Khasanov Alexander * Bugfix/random teams in created game (#539) * fix: Close div and change h7 to h6. * feat: Add back button to game_info page. * fix: Correct adding team to GameTeam. Delete mock-code for request to DS server. * fix: Refactor view-function send_game_video_to_process. * fix: Change url to send_game_video_to_process_view. * fix: Delete unusale import. * add spec of analytics in docs * add spec of analytics in docs * Feature/removing celery (#545) * game views in process * remove celery of game views * remove celery of player_views * remove celery * fix core.config.base * fix players-view * fix README * test prod workflow * fix /infra/prod/ * fix docker compose prod * test prod.dockerfile * test prod.dockerfile2 * test prod.dockerfile3 * test prod.dockerfile4 * test prod.dockerfile5 * test prod.dockerfile6 * test deploy to production * test deploy to production2 * test deploy to production3 * test deploy to production4 * test deploy to production6 * Relocated openpyxl config file to configs folder (#549) * Add commands export-db and import-db. (#548) * test deploy to production7 * test deploy to production8 * test deploy to production9 * test deploy to production10 * test deploy to production10 * test deploy to production11 * test deploy to production12 * test deploy to production13 * test deploy to production14 * test deploy to production15 * test deploy to production16 * test deploy to production17 * test deploy to production18 * test deploy to production19 * test deploy to production20 * test deploy to production21 * test deploy to production22 * test deploy to production23 * test deploy to production24 * test deploy to production25 * test deploy to production26 * test deploy to production27 * test secrets * test secrets1 * test secrets2 * test secrets3 * test secrets4 * test secrets5 * test secrets6 * test secrets7 * test copy infra to vps * test copy infra to vps - 1 * test copy infra to vps - 2 * test copy infra to vps - 3 * test copy infra to vps - 4 * test copy infra to vps - 5 * test copy infra to vps - 6 * test copy infra to vps - 7 * test copy infra to vps - 8 * test copy infra to vps - 9 * test copy infra to vps - 10 * test copy infra to vps - 11 * test copy infra to vps - 12 * Removed the video_api module and everything related to it * test deploy * test deploy1 * test deploy2 * test deploy3 * test deploy4 * test deploy5 * test deploy6 * test deploy7 * test deploy8 * test deploy8 * test deploy9 * test deploy10 * test deploy11 * test deploy12 * test deploy13 * test deploy14 * test deploy15 * test deploy16 * test deploy17 * test deploy18 * test deploy19 * test deploy20 * test deploy21 * pre-realease * pre-realease1 * pre-realease2 * pre-realease3 * pre-realease4 * pre-realease5 * pre-realease6 * pre-realease7 * pre-realease8 * pre-realease9 * pre-realease10 * pre-realease11 * on review * test build * test build1 * test build2 * test build3 * test watchtower * test watchtower1 * on review3 * on review4 * test build * test build1 * on review 4 * test build-1 * test build-2 * test build 5 * test build 6 * test build 6 * test build 7 * test build 8 * test build 8 * test build 9 * test build 10 * test build 11 * test build 12 * test build 14 * on_review * on_review final * on_review final * on_review final1 * Remove parser and rewrite command fill-db (#554) * fix by review --------- Co-authored-by: zaritskiyaa Co-authored-by: Rodion a vrode ne on <132199131+SHURSHALO@users.noreply.github.com> Co-authored-by: Nikita Smykov <132088678+Apicqq@users.noreply.github.com> Co-authored-by: Игорь Митяшин <132353894+ItsFreez@users.noreply.github.com> Co-authored-by: Salikov Nikita Co-authored-by: zaritskiiAA <166800734+zaritskiiAA@users.noreply.github.com> Co-authored-by: Milkyaway13 <129880524+Milkyaway13@users.noreply.github.com> Co-authored-by: Maxim Portnov <124585181+DoomHunter190@users.noreply.github.com> Co-authored-by: Максим Портнов Co-authored-by: Алексей Сосов~ Co-authored-by: posredn1k <114393753+posredn1k@users.noreply.github.com> Co-authored-by: trippiez <116493293+trippiez@users.noreply.github.com> Co-authored-by: Tom Bulmer Co-authored-by: PavelNep1996 <118282648+PavelNep1996@users.noreply.github.com> Co-authored-by: Borovkov Ilya <48968499+ffff00-korj@users.noreply.github.com> Co-authored-by: AleksandrPU Co-authored-by: ramil-khan <116222162+ramil-khan@users.noreply.github.com> Co-authored-by: RuselK <123992635+RuselK@users.noreply.github.com> Co-authored-by: AlexanderKhasanov Co-authored-by: Khasanov Alexander Co-authored-by: OlegGsk <142893688+OlegGsk@users.noreply.github.com> Co-authored-by: Олег Говоровский Co-authored-by: Konstantin Shperling (Toksi) <79082640+Toksi86@users.noreply.github.com> Co-authored-by: Иван <128288828+InKLaR1TY@users.noreply.github.com> Co-authored-by: Toksi86 --- .env.example | 40 + .github/ISSUE_TEMPLATE/bug_report.md | 20 + .github/ISSUE_TEMPLATE/docs-template.md | 17 + .github/ISSUE_TEMPLATE/new-feature.md | 20 + .github/PULL_REQUEST_TEMPLATE.md | 23 + .github/workflows/ codestyle_pep8.yaml | 29 + .github/workflows/prod_deploy.yaml | 133 + .github/workflows/pytest.yaml | 32 + .gitignore | 46 +- .pre-commit-config.yaml | 33 + Makefile | 142 + README.md | 777 +++- .../analytics/__init__.py | 0 adaptive_hockey_federation/analytics/apps.py | 8 + adaptive_hockey_federation/analytics/forms.py | 78 + .../analytics/schema.py | 6 + adaptive_hockey_federation/analytics/urls.py | 12 + adaptive_hockey_federation/analytics/views.py | 98 + .../competitions/__init__.py | 0 .../competitions/admin.py | 16 + .../competitions/apps.py | 9 + .../competitions/forms.py | 241 ++ .../competitions/migrations/0001_initial.py | 35 + .../competitions/migrations/__init__.py | 0 .../competitions/models.py | 73 + .../competitions/schema.py | 43 + .../competitions/urls.py | 42 + .../competitions/utils.py | 20 + .../competitions/validators.py | 16 + .../competitions/views.py | 442 +++ adaptive_hockey_federation/core/__init__.py | 0 adaptive_hockey_federation/core/apps.py | 8 + adaptive_hockey_federation/core/asgi.py | 10 + .../core/config/__init__.py | 0 .../core/config/base_settings.py | 172 + .../core/config/dev_settings.py | 94 + .../core/config/openpyxl/__init__.py | 0 .../core/config/openpyxl/settings.py | 39 + .../core/config/prod_settings.py | 82 + .../core/config/test_settings.py | 8 + adaptive_hockey_federation/core/constants.py | 195 + .../core/context_processors.py | 21 + .../core/fixtures/fill_random_diagnosis.py | 24 + adaptive_hockey_federation/core/logging.py | 9 + .../core/management/commands/__init__.py | 0 .../core/management/commands/export-db.py | 32 + .../core/management/commands/fill-db.py | 140 + .../core/management/commands/fill-test-db.py | 207 ++ .../management/commands/fill-test-stage.py | 33 + .../core/management/commands/import-db.py | 54 + .../core/migrations/__init__.py | 0 .../core/permissions.py | 12 + .../core/templatetags/__init__.py | 0 .../core/templatetags/user_filters.py | 8 + adaptive_hockey_federation/core/urls.py | 38 + adaptive_hockey_federation/core/utils.py | 182 + adaptive_hockey_federation/core/validators.py | 49 + adaptive_hockey_federation/core/views.py | 33 + adaptive_hockey_federation/core/wsgi.py | 10 + adaptive_hockey_federation/games/__init__.py | 0 adaptive_hockey_federation/games/admin.py | 135 + adaptive_hockey_federation/games/apps.py | 15 + adaptive_hockey_federation/games/constants.py | 54 + adaptive_hockey_federation/games/forms.py | 220 ++ .../games/migrations/0001_initial.py | 198 + .../games/migrations/0002_gamedataplayer.py | 48 + ...ter_gameplayer_unique_together_and_more.py | 27 + .../games/migrations/__init__.py | 0 adaptive_hockey_federation/games/mixins.py | 37 + adaptive_hockey_federation/games/models.py | 185 + adaptive_hockey_federation/games/signals.py | 55 + adaptive_hockey_federation/games/urls.py | 33 + adaptive_hockey_federation/games/views.py | 231 ++ adaptive_hockey_federation/main/__init__.py | 0 .../main/admin/__init__.py | 30 + .../main/admin/admin.py | 262 ++ .../main/admin/inlines.py | 48 + adaptive_hockey_federation/main/apps.py | 9 + .../main/controllers/__init__.py | 0 .../main/controllers/ajax.py | 29 + .../main/controllers/main_views.py | 59 + .../main/controllers/mixins.py | 10 + .../main/controllers/player_views.py | 410 +++ .../main/controllers/staff_views.py | 316 ++ .../main/controllers/team_views.py | 337 ++ .../main/controllers/utils.py | 26 + .../main/data_factories/__init__.py | 0 .../main/data_factories/factories.py | 309 ++ .../main/data_factories/utils.py | 58 + adaptive_hockey_federation/main/fields.py | 171 + adaptive_hockey_federation/main/forms.py | 440 +++ .../main/migrations/0001_initial.py | 218 ++ .../migrations/0002_setting_disciplines.py | 37 + .../main/migrations/0003_gamedataplayer.py | 27 + .../migrations/0004_delete_gamedataplayer.py | 16 + .../main/migrations/__init__.py | 0 adaptive_hockey_federation/main/mixins.py | 24 + adaptive_hockey_federation/main/models.py | 480 +++ .../main/permissions.py | 97 + .../main/schemas/__init__.py | 0 .../main/schemas/main_schema.py | 67 + .../main/schemas/player_schema.py | 70 + .../main/schemas/staff_schema.py | 60 + .../main/schemas/team_schema.py | 118 + adaptive_hockey_federation/main/urls.py | 141 + adaptive_hockey_federation/main/views.py | 7 + adaptive_hockey_federation/manage.py | 25 + .../service/a_hockey_requests.py | 25 + .../service/mock_ds_server/celeryconfig.py | 10 + .../service/mock_ds_server/constants.py | 5 + .../service/mock_ds_server/main.py | 68 + .../service/mock_ds_server/worker.py | 37 + .../service/video_processing.py | 30 + .../staticfiles/css/base_style.css | 1413 ++++++++ .../staticfiles/css/bootstrap.min.css | 6 + .../staticfiles/css/img/forma.jpg | Bin 0 -> 273858 bytes .../staticfiles/css/select.css | 589 +++ .../staticfiles/favicon.ico | Bin 0 -> 15406 bytes .../staticfiles/img/arrow.svg | 3 + .../staticfiles/img/delete.png | Bin 0 -> 750 bytes .../staticfiles/img/edit.png | Bin 0 -> 420 bytes .../staticfiles/img/ellipse.png | Bin 0 -> 7702 bytes .../staticfiles/img/fone.png | Bin 0 -> 255335 bytes .../staticfiles/img/footer.png | Bin 0 -> 51862 bytes .../staticfiles/img/icon-unknown.svg | 3 + .../staticfiles/img/logo.png | Bin 0 -> 42677 bytes .../staticfiles/img/rect.png | Bin 0 -> 149035 bytes .../staticfiles/img/search.svg | 3 + .../staticfiles/img/selector-icons.svg | 34 + .../staticfiles/js/bootstrap.bundle.min.js | 7 + .../staticfiles/js/date-picker.js | 43 + .../staticfiles/js/discipline-picker.js | 45 + .../staticfiles/js/jquery-select2.min.js | 3182 +++++++++++++++++ .../staticfiles/js/options-picker.js | 23 + .../js/player_form/discipline_levels.js | 17 + .../staticfiles/js/search.js | 75 + .../staticfiles/js/select.js | 84 + .../staticfiles/js/select2.min.js | 2495 +++++++++++++ .../staticfiles/js/tailwindvss-3.4.1.js | 63 + .../templates/admin/custom_change_form.html | 17 + .../templates/admin/custom_fieldset.html | 57 + .../templates/admin/custom_stacked.html | 29 + .../templates/analytics/analytics.html | 23 + .../templates/analytics/dashboard.html | 46 + .../templates/analytics/filters.html | 11 + .../templates/analytics/table.html | 46 + .../templates/base/active_check_icon.html | 3 + .../templates/base/active_delete_icon.html | 3 + .../templates/base/active_edit_icon.html | 3 + .../templates/base/base.html | 86 + .../base/base_edit_delete_buttons!.html | 18 + .../templates/base/button.html | 11 + .../templates/base/button_create.html | 11 + .../templates/base/button_list.html | 9 + .../templates/base/cell_button.html | 4 + .../templates/base/check_icon.html | 3 + .../base/competition_button_create.html | 2 + .../base/create_delete_button_players!.html | 16 + .../templates/base/delete_icon.html | 3 + .../templates/base/drawer.html | 61 + .../templates/base/edit_delete_buttons!.html | 9 + .../templates/base/edit_icon.html | 3 + .../templates/base/footer.html | 58 + .../templates/base/header.html | 21 + .../templates/base/messages.html | 12 + .../templates/base/pagination.html | 38 + .../templates/base/pagination_teams.html | 37 + .../templates/base/return_button.html | 1 + .../templates/base/search_form.html | 64 + .../templates/base/team_button_create.html | 4 + .../base/team_edit_delete_buttons!.html | 1 + .../templates/base/triangle.html | 5 + .../templates/base/user_button_create!.html | 2 + .../base/user_edit_delete_buttons!.html | 1 + .../templates/emailing/email.html | 377 ++ .../emailing/password_reset_email.html | 13 + .../templates/emailing/welcome_letter.html | 26 + .../templates/error-pages/403.html | 4 + .../templates/error-pages/404.html | 4 + .../templates/error-pages/500.html | 4 + .../error-pages/base_error_page.html | 17 + .../base_edit_delete_buttons.html | 18 + .../competitions/competition_create_edit.html | 49 + .../competition_edit_delete_buttons.html | 1 + .../main/competitions/competitions.html | 7 + .../competitions_team_search_field.html | 7 + .../templates/main/competitions/table.html | 67 + .../main/competitions_id/competitions_id.html | 50 + .../components/competition_teams_table.html | 17 + .../competition_teams_table_base.html | 31 + .../competitions_teams_table_available.html | 17 + .../main/fields/check_box_field.html | 7 + .../templates/main/fields/city_field.html | 17 + .../templates/main/fields/date_field.html | 11 + .../main/fields/diagnosis_field.html | 17 + .../main/fields/discipline_select_field.html | 38 + .../main/fields/documents_field.html | 5 + .../templates/main/fields/select_field.html | 15 + .../fields/team_select_field_1_field.html | 26 + .../fields/team_select_field_2_fields.html | 38 + .../main/fields/text_input_field.html | 16 + .../main/games/base_edit_delete_buttons.html | 19 + .../main/games/game_button_create.html | 2 + .../main/games/game_create_edit.html | 65 + .../templates/main/games/game_detail.html | 90 + .../main/games/game_edit_delete_buttons.html | 1 + .../templates/main/games/games.html | 7 + .../main/games/player_number_edit.html | 58 + .../templates/main/games/table.html | 54 + .../templates/main/home/main.html | 7 + .../templates/main/home/search_form.html | 11 + .../templates/main/home/table.html | 41 + .../MOCK_player_id_video_response.html | 10 + .../main/player_id/edit_delete_buttons.html | 11 + .../templates/main/player_id/player_id.html | 77 + .../main/player_id/player_id_create_edit.html | 135 + .../main/player_id/player_id_deleted.html | 11 + .../main/player_id/player_id_video_games.html | 8 + .../main/player_id/video_games_button.html | 9 + .../templates/main/players/cell_button.html | 4 + .../players/create_delete_button_players.html | 16 + .../main/players/create_delete_buttons.html | 17 + .../main/players/player_button_create.html | 4 + .../templates/main/players/players.html | 7 + .../templates/main/players/table.html | 68 + .../templates/main/staffs/add_button.html | 9 + .../main/staffs/edit_delete_buttons.html | 18 + .../main/staffs/edit_delete_team_buttons.html | 28 + .../main/staffs/staff_button_create.html | 2 + .../templates/main/staffs/staff_id.html | 70 + .../main/staffs/staff_id_create_edit.html | 38 + .../staffs/staff_id_team_edit_create.html | 50 + .../templates/main/staffs/staffs.html | 7 + .../staffs/staffs_edit_delete_buttons.html | 16 + .../templates/main/staffs/table.html | 59 + .../main/teams/base_edit_delete_buttons.html | 24 + .../templates/main/teams/cell_button.html | 4 + .../main/teams/create_delete_buttons.html | 24 + .../templates/main/teams/filters.html | 11 + .../templates/main/teams/table.html | 47 + .../main/teams/team_create_edit.html | 51 + .../main/teams/team_edit_delete_buttons.html | 1 + .../teams/team_form_city_field_datalist.html | 7 + .../templates/main/teams/teams.html | 12 + .../teams_id/button_create_team_member.html | 9 + .../templates/main/teams_id/cell_button.html | 4 + .../components/staff_search_field.html | 7 + .../main/teams_id/create_delete_buttons.html | 19 + .../teams_id/edit_delete_button_players.html | 18 + .../teams_id/edit_delete_button_staffs.html | 24 + .../templates/main/teams_id/table.html | 67 + .../templates/main/teams_id/teams_id.html | 54 + .../main/unloads/base_delete_buttons.html | 12 + .../templates/main/unloads/table.html | 41 + .../templates/main/unloads/unloads.html | 7 + .../main/unloads/unloads_delete.html | 1 + .../main/users/create_delete_buttons.html | 16 + .../templates/main/users/list.html | 7 + .../templates/main/users/table.html | 55 + .../main/users/user_create_edit.html | 84 + .../templates/main/users/users_buttons.html | 17 + .../templates/registration/base.html | 19 + .../registration/includes/close_divs.html | 2 + .../registration/includes/fields_form.html | 8 + .../registration/includes/footer.html | 10 + .../registration/includes/forms_errors.html | 14 + .../templates/registration/includes/head.html | 5 + .../registration/includes/header.html | 9 + .../registration/includes/open_divs.html | 2 + .../templates/registration/login.html | 34 + .../registration/password_change_done.html | 12 + .../registration/password_change_form.html | 23 + .../registration/password_reset_complete.html | 19 + .../registration/password_reset_confirm.html | 33 + .../registration/password_reset_done.html | 15 + .../registration/password_reset_email.html | 12 + .../registration/password_reset_form.html | 25 + adaptive_hockey_federation/tests/__init__.py | 0 adaptive_hockey_federation/tests/base.py | 1034 ++++++ .../tests/command_test.py | 46 + adaptive_hockey_federation/tests/crud_test.py | 916 +++++ .../tests/fixture_user.py | 49 + .../tests/model_schemas/__init__.py | 0 .../tests/model_schemas/city.py | 37 + .../tests/model_schemas/diagnosis.py | 44 + .../tests/model_schemas/discipline_level.py | 42 + .../tests/model_schemas/discipline_name.py | 3 + .../fields_validation_schemas.py | 140 + .../tests/model_schemas/generics.py | 43 + .../tests/model_schemas/group.py | 26 + .../tests/model_schemas/nosology.py | 3 + .../tests/model_schemas/player.py | 99 + .../tests/model_schemas/staff_member.py | 71 + .../tests/model_schemas/staff_team_member.py | 39 + .../tests/model_schemas/team.py | 42 + .../tests/model_schemas/user.py | 84 + .../tests/permissions_test.py | 343 ++ .../tests/player_test.py | 120 + .../tests/smoke_test.py | 238 ++ .../tests/url_schema.py | 254 ++ adaptive_hockey_federation/tests/url_test.py | 326 ++ adaptive_hockey_federation/tests/utils.py | 298 ++ .../unloads/__init__.py | 0 adaptive_hockey_federation/unloads/admin.py | 13 + adaptive_hockey_federation/unloads/apps.py | 9 + .../unloads/factories.py | 50 + adaptive_hockey_federation/unloads/mapping.py | 16 + .../unloads/migrations/0001_initial.py | 34 + .../unloads/migrations/__init__.py | 0 adaptive_hockey_federation/unloads/models.py | 47 + adaptive_hockey_federation/unloads/urls.py | 22 + adaptive_hockey_federation/unloads/utils.py | 135 + adaptive_hockey_federation/unloads/views.py | 172 + adaptive_hockey_federation/users/__init__.py | 0 adaptive_hockey_federation/users/admin.py | 98 + adaptive_hockey_federation/users/apps.py | 9 + adaptive_hockey_federation/users/constants.py | 55 + adaptive_hockey_federation/users/factories.py | 37 + adaptive_hockey_federation/users/forms.py | 208 ++ adaptive_hockey_federation/users/managers.py | 40 + .../users/migrations/0001_initial.py | 68 + .../users/migrations/0002__setting_group.py | 46 + .../users/migrations/__init__.py | 0 adaptive_hockey_federation/users/models.py | 227 ++ adaptive_hockey_federation/users/provaders.py | 28 + adaptive_hockey_federation/users/urls.py | 42 + .../users/utilits/__init__.py | 0 .../users/utilits/create_password.py | 8 + .../users/utilits/render.py | 22 + .../users/utilits/send_mails.py | 126 + .../users/validators.py | 13 + adaptive_hockey_federation/users/views.py | 201 ++ docs/ER_Diagram.drawio.jpg | Bin 0 -> 278946 bytes docs/ER_Diagram.drawio.xml | 2916 +++++++++++++++ docs/codestyle.md | 188 + docs/diagram_video_workers.bpmn | 239 ++ docs/diagram_video_workers.bpmn.svg | 4 + docs/materials/instructions.md | 60 + docs/specification_of_analysts.md | 492 +++ infra/dev/docker-compose.dev.yaml | 21 + infra/nginx/nginx_stage.conf | 39 + infra/prod/adaptive_hockey_federation.service | 31 + infra/prod/docker-compose.prod.yaml | 57 + infra/prod/prod.Dockerfile | 12 + .../stage/adaptive_hockey_federation.service | 34 + infra/stage/docker-compose.stage.yaml | 57 + infra/stage/stage.Dockerfile | 13 + poetry.lock | 2344 +++++++++++- pyproject.toml | 138 +- pytest.ini | 6 + requirements/develop.txt | 84 + requirements/production.txt | 100 + setup.cfg | 17 + 353 files changed, 33550 insertions(+), 146 deletions(-) create mode 100644 .env.example create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/docs-template.md create mode 100644 .github/ISSUE_TEMPLATE/new-feature.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/ codestyle_pep8.yaml create mode 100644 .github/workflows/prod_deploy.yaml create mode 100644 .github/workflows/pytest.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 Makefile create mode 100644 adaptive_hockey_federation/analytics/__init__.py create mode 100644 adaptive_hockey_federation/analytics/apps.py create mode 100644 adaptive_hockey_federation/analytics/forms.py create mode 100644 adaptive_hockey_federation/analytics/schema.py create mode 100644 adaptive_hockey_federation/analytics/urls.py create mode 100644 adaptive_hockey_federation/analytics/views.py create mode 100644 adaptive_hockey_federation/competitions/__init__.py create mode 100644 adaptive_hockey_federation/competitions/admin.py create mode 100644 adaptive_hockey_federation/competitions/apps.py create mode 100644 adaptive_hockey_federation/competitions/forms.py create mode 100644 adaptive_hockey_federation/competitions/migrations/0001_initial.py create mode 100644 adaptive_hockey_federation/competitions/migrations/__init__.py create mode 100644 adaptive_hockey_federation/competitions/models.py create mode 100644 adaptive_hockey_federation/competitions/schema.py create mode 100644 adaptive_hockey_federation/competitions/urls.py create mode 100644 adaptive_hockey_federation/competitions/utils.py create mode 100644 adaptive_hockey_federation/competitions/validators.py create mode 100644 adaptive_hockey_federation/competitions/views.py create mode 100644 adaptive_hockey_federation/core/__init__.py create mode 100644 adaptive_hockey_federation/core/apps.py create mode 100644 adaptive_hockey_federation/core/asgi.py create mode 100644 adaptive_hockey_federation/core/config/__init__.py create mode 100644 adaptive_hockey_federation/core/config/base_settings.py create mode 100644 adaptive_hockey_federation/core/config/dev_settings.py create mode 100644 adaptive_hockey_federation/core/config/openpyxl/__init__.py create mode 100644 adaptive_hockey_federation/core/config/openpyxl/settings.py create mode 100644 adaptive_hockey_federation/core/config/prod_settings.py create mode 100644 adaptive_hockey_federation/core/config/test_settings.py create mode 100644 adaptive_hockey_federation/core/constants.py create mode 100644 adaptive_hockey_federation/core/context_processors.py create mode 100644 adaptive_hockey_federation/core/fixtures/fill_random_diagnosis.py create mode 100644 adaptive_hockey_federation/core/logging.py create mode 100644 adaptive_hockey_federation/core/management/commands/__init__.py create mode 100644 adaptive_hockey_federation/core/management/commands/export-db.py create mode 100644 adaptive_hockey_federation/core/management/commands/fill-db.py create mode 100644 adaptive_hockey_federation/core/management/commands/fill-test-db.py create mode 100644 adaptive_hockey_federation/core/management/commands/fill-test-stage.py create mode 100644 adaptive_hockey_federation/core/management/commands/import-db.py create mode 100644 adaptive_hockey_federation/core/migrations/__init__.py create mode 100644 adaptive_hockey_federation/core/permissions.py create mode 100644 adaptive_hockey_federation/core/templatetags/__init__.py create mode 100644 adaptive_hockey_federation/core/templatetags/user_filters.py create mode 100644 adaptive_hockey_federation/core/urls.py create mode 100644 adaptive_hockey_federation/core/utils.py create mode 100644 adaptive_hockey_federation/core/validators.py create mode 100644 adaptive_hockey_federation/core/views.py create mode 100644 adaptive_hockey_federation/core/wsgi.py create mode 100644 adaptive_hockey_federation/games/__init__.py create mode 100644 adaptive_hockey_federation/games/admin.py create mode 100644 adaptive_hockey_federation/games/apps.py create mode 100644 adaptive_hockey_federation/games/constants.py create mode 100644 adaptive_hockey_federation/games/forms.py create mode 100644 adaptive_hockey_federation/games/migrations/0001_initial.py create mode 100644 adaptive_hockey_federation/games/migrations/0002_gamedataplayer.py create mode 100644 adaptive_hockey_federation/games/migrations/0003_alter_gameplayer_unique_together_and_more.py create mode 100644 adaptive_hockey_federation/games/migrations/__init__.py create mode 100644 adaptive_hockey_federation/games/mixins.py create mode 100644 adaptive_hockey_federation/games/models.py create mode 100644 adaptive_hockey_federation/games/signals.py create mode 100644 adaptive_hockey_federation/games/urls.py create mode 100644 adaptive_hockey_federation/games/views.py create mode 100644 adaptive_hockey_federation/main/__init__.py create mode 100644 adaptive_hockey_federation/main/admin/__init__.py create mode 100644 adaptive_hockey_federation/main/admin/admin.py create mode 100644 adaptive_hockey_federation/main/admin/inlines.py create mode 100644 adaptive_hockey_federation/main/apps.py create mode 100644 adaptive_hockey_federation/main/controllers/__init__.py create mode 100644 adaptive_hockey_federation/main/controllers/ajax.py create mode 100644 adaptive_hockey_federation/main/controllers/main_views.py create mode 100644 adaptive_hockey_federation/main/controllers/mixins.py create mode 100644 adaptive_hockey_federation/main/controllers/player_views.py create mode 100644 adaptive_hockey_federation/main/controllers/staff_views.py create mode 100644 adaptive_hockey_federation/main/controllers/team_views.py create mode 100644 adaptive_hockey_federation/main/controllers/utils.py create mode 100644 adaptive_hockey_federation/main/data_factories/__init__.py create mode 100644 adaptive_hockey_federation/main/data_factories/factories.py create mode 100644 adaptive_hockey_federation/main/data_factories/utils.py create mode 100644 adaptive_hockey_federation/main/fields.py create mode 100644 adaptive_hockey_federation/main/forms.py create mode 100644 adaptive_hockey_federation/main/migrations/0001_initial.py create mode 100644 adaptive_hockey_federation/main/migrations/0002_setting_disciplines.py create mode 100644 adaptive_hockey_federation/main/migrations/0003_gamedataplayer.py create mode 100644 adaptive_hockey_federation/main/migrations/0004_delete_gamedataplayer.py create mode 100644 adaptive_hockey_federation/main/migrations/__init__.py create mode 100644 adaptive_hockey_federation/main/mixins.py create mode 100644 adaptive_hockey_federation/main/models.py create mode 100644 adaptive_hockey_federation/main/permissions.py create mode 100644 adaptive_hockey_federation/main/schemas/__init__.py create mode 100644 adaptive_hockey_federation/main/schemas/main_schema.py create mode 100644 adaptive_hockey_federation/main/schemas/player_schema.py create mode 100644 adaptive_hockey_federation/main/schemas/staff_schema.py create mode 100644 adaptive_hockey_federation/main/schemas/team_schema.py create mode 100644 adaptive_hockey_federation/main/urls.py create mode 100644 adaptive_hockey_federation/main/views.py create mode 100644 adaptive_hockey_federation/manage.py create mode 100644 adaptive_hockey_federation/service/a_hockey_requests.py create mode 100644 adaptive_hockey_federation/service/mock_ds_server/celeryconfig.py create mode 100644 adaptive_hockey_federation/service/mock_ds_server/constants.py create mode 100644 adaptive_hockey_federation/service/mock_ds_server/main.py create mode 100644 adaptive_hockey_federation/service/mock_ds_server/worker.py create mode 100644 adaptive_hockey_federation/service/video_processing.py create mode 100644 adaptive_hockey_federation/staticfiles/css/base_style.css create mode 100644 adaptive_hockey_federation/staticfiles/css/bootstrap.min.css create mode 100644 adaptive_hockey_federation/staticfiles/css/img/forma.jpg create mode 100644 adaptive_hockey_federation/staticfiles/css/select.css create mode 100644 adaptive_hockey_federation/staticfiles/favicon.ico create mode 100644 adaptive_hockey_federation/staticfiles/img/arrow.svg create mode 100644 adaptive_hockey_federation/staticfiles/img/delete.png create mode 100644 adaptive_hockey_federation/staticfiles/img/edit.png create mode 100644 adaptive_hockey_federation/staticfiles/img/ellipse.png create mode 100644 adaptive_hockey_federation/staticfiles/img/fone.png create mode 100644 adaptive_hockey_federation/staticfiles/img/footer.png create mode 100644 adaptive_hockey_federation/staticfiles/img/icon-unknown.svg create mode 100644 adaptive_hockey_federation/staticfiles/img/logo.png create mode 100644 adaptive_hockey_federation/staticfiles/img/rect.png create mode 100644 adaptive_hockey_federation/staticfiles/img/search.svg create mode 100644 adaptive_hockey_federation/staticfiles/img/selector-icons.svg create mode 100644 adaptive_hockey_federation/staticfiles/js/bootstrap.bundle.min.js create mode 100644 adaptive_hockey_federation/staticfiles/js/date-picker.js create mode 100644 adaptive_hockey_federation/staticfiles/js/discipline-picker.js create mode 100644 adaptive_hockey_federation/staticfiles/js/jquery-select2.min.js create mode 100644 adaptive_hockey_federation/staticfiles/js/options-picker.js create mode 100644 adaptive_hockey_federation/staticfiles/js/player_form/discipline_levels.js create mode 100644 adaptive_hockey_federation/staticfiles/js/search.js create mode 100644 adaptive_hockey_federation/staticfiles/js/select.js create mode 100644 adaptive_hockey_federation/staticfiles/js/select2.min.js create mode 100644 adaptive_hockey_federation/staticfiles/js/tailwindvss-3.4.1.js create mode 100644 adaptive_hockey_federation/templates/admin/custom_change_form.html create mode 100644 adaptive_hockey_federation/templates/admin/custom_fieldset.html create mode 100644 adaptive_hockey_federation/templates/admin/custom_stacked.html create mode 100644 adaptive_hockey_federation/templates/analytics/analytics.html create mode 100644 adaptive_hockey_federation/templates/analytics/dashboard.html create mode 100644 adaptive_hockey_federation/templates/analytics/filters.html create mode 100644 adaptive_hockey_federation/templates/analytics/table.html create mode 100644 adaptive_hockey_federation/templates/base/active_check_icon.html create mode 100644 adaptive_hockey_federation/templates/base/active_delete_icon.html create mode 100644 adaptive_hockey_federation/templates/base/active_edit_icon.html create mode 100644 adaptive_hockey_federation/templates/base/base.html create mode 100644 adaptive_hockey_federation/templates/base/base_edit_delete_buttons!.html create mode 100644 adaptive_hockey_federation/templates/base/button.html create mode 100644 adaptive_hockey_federation/templates/base/button_create.html create mode 100644 adaptive_hockey_federation/templates/base/button_list.html create mode 100644 adaptive_hockey_federation/templates/base/cell_button.html create mode 100644 adaptive_hockey_federation/templates/base/check_icon.html create mode 100644 adaptive_hockey_federation/templates/base/competition_button_create.html create mode 100644 adaptive_hockey_federation/templates/base/create_delete_button_players!.html create mode 100644 adaptive_hockey_federation/templates/base/delete_icon.html create mode 100644 adaptive_hockey_federation/templates/base/drawer.html create mode 100644 adaptive_hockey_federation/templates/base/edit_delete_buttons!.html create mode 100644 adaptive_hockey_federation/templates/base/edit_icon.html create mode 100644 adaptive_hockey_federation/templates/base/footer.html create mode 100644 adaptive_hockey_federation/templates/base/header.html create mode 100644 adaptive_hockey_federation/templates/base/messages.html create mode 100644 adaptive_hockey_federation/templates/base/pagination.html create mode 100644 adaptive_hockey_federation/templates/base/pagination_teams.html create mode 100644 adaptive_hockey_federation/templates/base/return_button.html create mode 100644 adaptive_hockey_federation/templates/base/search_form.html create mode 100644 adaptive_hockey_federation/templates/base/team_button_create.html create mode 100644 adaptive_hockey_federation/templates/base/team_edit_delete_buttons!.html create mode 100644 adaptive_hockey_federation/templates/base/triangle.html create mode 100644 adaptive_hockey_federation/templates/base/user_button_create!.html create mode 100644 adaptive_hockey_federation/templates/base/user_edit_delete_buttons!.html create mode 100644 adaptive_hockey_federation/templates/emailing/email.html create mode 100644 adaptive_hockey_federation/templates/emailing/password_reset_email.html create mode 100644 adaptive_hockey_federation/templates/emailing/welcome_letter.html create mode 100644 adaptive_hockey_federation/templates/error-pages/403.html create mode 100644 adaptive_hockey_federation/templates/error-pages/404.html create mode 100644 adaptive_hockey_federation/templates/error-pages/500.html create mode 100644 adaptive_hockey_federation/templates/error-pages/base_error_page.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/base_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/competition_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/competition_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/competitions.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/competitions_team_search_field.html create mode 100644 adaptive_hockey_federation/templates/main/competitions/table.html create mode 100644 adaptive_hockey_federation/templates/main/competitions_id/competitions_id.html create mode 100644 adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table.html create mode 100644 adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table_base.html create mode 100644 adaptive_hockey_federation/templates/main/competitions_id/components/competitions_teams_table_available.html create mode 100644 adaptive_hockey_federation/templates/main/fields/check_box_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/city_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/date_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/diagnosis_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/discipline_select_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/documents_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/select_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/team_select_field_1_field.html create mode 100644 adaptive_hockey_federation/templates/main/fields/team_select_field_2_fields.html create mode 100644 adaptive_hockey_federation/templates/main/fields/text_input_field.html create mode 100644 adaptive_hockey_federation/templates/main/games/base_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/games/game_button_create.html create mode 100644 adaptive_hockey_federation/templates/main/games/game_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/games/game_detail.html create mode 100644 adaptive_hockey_federation/templates/main/games/game_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/games/games.html create mode 100644 adaptive_hockey_federation/templates/main/games/player_number_edit.html create mode 100644 adaptive_hockey_federation/templates/main/games/table.html create mode 100644 adaptive_hockey_federation/templates/main/home/main.html create mode 100644 adaptive_hockey_federation/templates/main/home/search_form.html create mode 100644 adaptive_hockey_federation/templates/main/home/table.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/MOCK_player_id_video_response.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/player_id.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/player_id_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/player_id_deleted.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/player_id_video_games.html create mode 100644 adaptive_hockey_federation/templates/main/player_id/video_games_button.html create mode 100644 adaptive_hockey_federation/templates/main/players/cell_button.html create mode 100644 adaptive_hockey_federation/templates/main/players/create_delete_button_players.html create mode 100644 adaptive_hockey_federation/templates/main/players/create_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/players/player_button_create.html create mode 100644 adaptive_hockey_federation/templates/main/players/players.html create mode 100644 adaptive_hockey_federation/templates/main/players/table.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/add_button.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/edit_delete_team_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staff_button_create.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staff_id.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staff_id_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staff_id_team_edit_create.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staffs.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/staffs_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/staffs/table.html create mode 100644 adaptive_hockey_federation/templates/main/teams/base_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/teams/cell_button.html create mode 100644 adaptive_hockey_federation/templates/main/teams/create_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/teams/filters.html create mode 100644 adaptive_hockey_federation/templates/main/teams/table.html create mode 100644 adaptive_hockey_federation/templates/main/teams/team_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/teams/team_edit_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/teams/team_form_city_field_datalist.html create mode 100644 adaptive_hockey_federation/templates/main/teams/teams.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/button_create_team_member.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/cell_button.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/components/staff_search_field.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/create_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_players.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_staffs.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/table.html create mode 100644 adaptive_hockey_federation/templates/main/teams_id/teams_id.html create mode 100644 adaptive_hockey_federation/templates/main/unloads/base_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/unloads/table.html create mode 100644 adaptive_hockey_federation/templates/main/unloads/unloads.html create mode 100644 adaptive_hockey_federation/templates/main/unloads/unloads_delete.html create mode 100644 adaptive_hockey_federation/templates/main/users/create_delete_buttons.html create mode 100644 adaptive_hockey_federation/templates/main/users/list.html create mode 100644 adaptive_hockey_federation/templates/main/users/table.html create mode 100644 adaptive_hockey_federation/templates/main/users/user_create_edit.html create mode 100644 adaptive_hockey_federation/templates/main/users/users_buttons.html create mode 100644 adaptive_hockey_federation/templates/registration/base.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/close_divs.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/fields_form.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/footer.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/forms_errors.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/head.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/header.html create mode 100644 adaptive_hockey_federation/templates/registration/includes/open_divs.html create mode 100644 adaptive_hockey_federation/templates/registration/login.html create mode 100644 adaptive_hockey_federation/templates/registration/password_change_done.html create mode 100644 adaptive_hockey_federation/templates/registration/password_change_form.html create mode 100644 adaptive_hockey_federation/templates/registration/password_reset_complete.html create mode 100644 adaptive_hockey_federation/templates/registration/password_reset_confirm.html create mode 100644 adaptive_hockey_federation/templates/registration/password_reset_done.html create mode 100644 adaptive_hockey_federation/templates/registration/password_reset_email.html create mode 100644 adaptive_hockey_federation/templates/registration/password_reset_form.html create mode 100644 adaptive_hockey_federation/tests/__init__.py create mode 100644 adaptive_hockey_federation/tests/base.py create mode 100644 adaptive_hockey_federation/tests/command_test.py create mode 100644 adaptive_hockey_federation/tests/crud_test.py create mode 100644 adaptive_hockey_federation/tests/fixture_user.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/__init__.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/city.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/diagnosis.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/discipline_level.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/discipline_name.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/fields_validation_schemas.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/generics.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/group.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/nosology.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/player.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/staff_member.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/staff_team_member.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/team.py create mode 100644 adaptive_hockey_federation/tests/model_schemas/user.py create mode 100644 adaptive_hockey_federation/tests/permissions_test.py create mode 100644 adaptive_hockey_federation/tests/player_test.py create mode 100644 adaptive_hockey_federation/tests/smoke_test.py create mode 100644 adaptive_hockey_federation/tests/url_schema.py create mode 100644 adaptive_hockey_federation/tests/url_test.py create mode 100644 adaptive_hockey_federation/tests/utils.py create mode 100644 adaptive_hockey_federation/unloads/__init__.py create mode 100644 adaptive_hockey_federation/unloads/admin.py create mode 100644 adaptive_hockey_federation/unloads/apps.py create mode 100644 adaptive_hockey_federation/unloads/factories.py create mode 100644 adaptive_hockey_federation/unloads/mapping.py create mode 100644 adaptive_hockey_federation/unloads/migrations/0001_initial.py create mode 100644 adaptive_hockey_federation/unloads/migrations/__init__.py create mode 100644 adaptive_hockey_federation/unloads/models.py create mode 100644 adaptive_hockey_federation/unloads/urls.py create mode 100644 adaptive_hockey_federation/unloads/utils.py create mode 100644 adaptive_hockey_federation/unloads/views.py create mode 100644 adaptive_hockey_federation/users/__init__.py create mode 100644 adaptive_hockey_federation/users/admin.py create mode 100644 adaptive_hockey_federation/users/apps.py create mode 100644 adaptive_hockey_federation/users/constants.py create mode 100644 adaptive_hockey_federation/users/factories.py create mode 100644 adaptive_hockey_federation/users/forms.py create mode 100644 adaptive_hockey_federation/users/managers.py create mode 100644 adaptive_hockey_federation/users/migrations/0001_initial.py create mode 100644 adaptive_hockey_federation/users/migrations/0002__setting_group.py create mode 100644 adaptive_hockey_federation/users/migrations/__init__.py create mode 100644 adaptive_hockey_federation/users/models.py create mode 100644 adaptive_hockey_federation/users/provaders.py create mode 100644 adaptive_hockey_federation/users/urls.py create mode 100644 adaptive_hockey_federation/users/utilits/__init__.py create mode 100644 adaptive_hockey_federation/users/utilits/create_password.py create mode 100644 adaptive_hockey_federation/users/utilits/render.py create mode 100644 adaptive_hockey_federation/users/utilits/send_mails.py create mode 100644 adaptive_hockey_federation/users/validators.py create mode 100644 adaptive_hockey_federation/users/views.py create mode 100644 docs/ER_Diagram.drawio.jpg create mode 100644 docs/ER_Diagram.drawio.xml create mode 100644 docs/codestyle.md create mode 100644 docs/diagram_video_workers.bpmn create mode 100644 docs/diagram_video_workers.bpmn.svg create mode 100644 docs/materials/instructions.md create mode 100644 docs/specification_of_analysts.md create mode 100644 infra/dev/docker-compose.dev.yaml create mode 100644 infra/nginx/nginx_stage.conf create mode 100644 infra/prod/adaptive_hockey_federation.service create mode 100644 infra/prod/docker-compose.prod.yaml create mode 100644 infra/prod/prod.Dockerfile create mode 100644 infra/stage/adaptive_hockey_federation.service create mode 100644 infra/stage/docker-compose.stage.yaml create mode 100644 infra/stage/stage.Dockerfile create mode 100644 pytest.ini create mode 100644 requirements/develop.txt create mode 100644 requirements/production.txt create mode 100644 setup.cfg diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..4607bf07 --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# API settings +API_DOCS_KEY=8f2d9e1b2c4e6f +# Django settings +SECRET_KEY=12345678 +DEBUG=True +ALLOWED_HOSTS=* +# Superuser settings +DJANGO_SUPERUSER_FIRST_NAME=Админ +DJANGO_SUPERUSER_LAST_NAME=Админ +DJANGO_SUPERUSER_ROLE=Администратор +DJANGO_SUPERUSER_USERNAME=admin +DJANGO_SUPERUSER_EMAIL=admin@admin.ru +DJANGO_SUPERUSER_PASSWORD=admin +# Postgres settings +DB_ENGINE='django.db.backends.postgresql' +POSTGRES_PASSWORD='1qw2#ER$' +POSTGRES_USER='django' +POSTGRES_DB='django' +DB_HOST='localhost' +DB_PORT='5432' +# Email settings +EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST='smtp.yandex.ru' +EMAIL_PORT=587 +# реквизиты для аутентификации в почтовом сервисе +EMAIL_HOST_USER=your_mail@yandex.ru +EMAIL_HOST_PASSWORD=your_mail_password +EMAIL_USE_TLS=True +# Базовый урл сервиса по обработке видео +PROCESSING_SERVICE_BASE_URL=http://127.0.0.1:8010/ +#Redis +REDIS_BROKER_DATABASE_LEVEL=0 +# Celery +SETTINGS_LEVEL=dev + +CELERY_BROKER_HOST=localhost +CELERY_BROKER_PORT=6379 +CELERY_VISIBILITY_TIMEOUT=360 +# Yandex Disk settings +YANDEX_DISK_OAUTH_TOKEN=ya_oauth_token diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..057eed86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Шаблон для описания бага в приложении +title: '' +labels: bug +assignees: '' + +--- + +# Где был обнаружен баг ? + +*Описание места и возможных причин бага.* + +# Скриншоты + +*Сюда добавить возможные скрины с описанием.* + +# Предложения и предположения как исправить + +*Возможные догадки, как исправить.* diff --git a/.github/ISSUE_TEMPLATE/docs-template.md b/.github/ISSUE_TEMPLATE/docs-template.md new file mode 100644 index 00000000..799aa4c1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs-template.md @@ -0,0 +1,17 @@ +--- +name: Docs template +about: Оформление Readme и документации +title: '' +labels: documentation +assignees: '' + +--- + +# Что нужно описать? + +*Какую часть функционала описываем.* + + +# Где? + +*В какой части репозитория пишем.* diff --git a/.github/ISSUE_TEMPLATE/new-feature.md b/.github/ISSUE_TEMPLATE/new-feature.md new file mode 100644 index 00000000..138071c2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-feature.md @@ -0,0 +1,20 @@ +--- +name: New feature +about: Шаблон на реализацию нового функционала +title: '' +labels: enhancement +assignees: '' + +--- + +# Зачем? + +*Зачем выполняется реализация задачи и какую проблему она решает.* + +# Как нужно делать? + +*Как мы выполняем реализацию данной задачи, какие действия нужны для достижения цели.* + +# Где? + +*Где делаем реализацию задачи, какие сервисы задействованы, ссылка на требования, сcылка на API, скриншоты.* diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..c6d5243b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +# Description + +Если нужно что-то дополнить по внесённым изменениям. + +## Type of change + +Пожалуйста, удалите варианты, которые не относятся к ПР-у. + +- [ ] Documentation (опечатки, примеры кода или любое обновление документации) +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +# How Has This Been Tested? + +Опишите, пожалуйста, тесты, которые вы провели для проверки ваших изменений. Предоставьте инструкции, чтобы мы могли воспроизвести их. Также укажите все необходимые детали конфигурации тестов. + +## Checklist: + +- [ ] Мой код соответствует code-style данного проекта +- [ ] Я провел самоанализ собственного кода +- [ ] Я внес соответствующие изменения в документацию diff --git a/.github/workflows/ codestyle_pep8.yaml b/.github/workflows/ codestyle_pep8.yaml new file mode 100644 index 00000000..40561aad --- /dev/null +++ b/.github/workflows/ codestyle_pep8.yaml @@ -0,0 +1,29 @@ +name: Codestyle_pep8 + +on: [push, pull_request] + +jobs: + ruff: + runs-on: ubuntu-latest + name: ruff + steps: + - name: Установка Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Установка Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Извлечение репозитория + uses: actions/checkout@v4 + + - name: Установка зависимостей + run: | + poetry install + + - name: ruff + run: | + poetry run ruff check diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml new file mode 100644 index 00000000..3f8fab04 --- /dev/null +++ b/.github/workflows/prod_deploy.yaml @@ -0,0 +1,133 @@ +name: Production deploy + +on: + push: + branches: + - master + +env: + DEPLOY_PATH: adaptive_hockey_federation + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +defaults: + run: + working-directory: . + +jobs: + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation + + build_and_push: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + needs: pytest + + steps: + - uses: actions/checkout@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image for Production + uses: docker/build-push-action@v5 + with: + context: . + file: infra/prod/prod.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + + deploy: + if: github.ref == 'refs/heads/master' + name: Deploy changes on server + needs: [pytest, build_and_push] + runs-on: ubuntu-latest + environment: + name: prod_deploy + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Delete stage & dev + run: | + rm -r infra/stage + rm -r infra/dev + + - name: Copy infra via ssh + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + source: "infra/" + target: "${{ env.DEPLOY_PATH }}/infra" + rm: true + strip_components: 1 + + - name: Execute commands on VPS + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + script: | + cd ${{ env.DEPLOY_PATH }} + touch .env + + echo "${{ secrets.ENV_FILE }}" > .env + + cd infra/prod/ + sudo systemctl stop adaptive_hockey_federation.service + docker system prune --force + + sudo cp -f /home/production/adaptive_hockey_federation/infra/prod/adaptive_hockey_federation.service /etc/systemd/system/adaptive_hockey_federation.service + sudo systemctl daemon-reload + sudo systemctl start adaptive_hockey_federation.service + + sudo systemctl is-active --quiet adaptive_hockey_federation.service + until [ $? -eq 0 ]; do + echo "Waiting for adaptive_hockey_federation.service to be active..." + sleep 5 + sudo systemctl is-active --quiet adaptive_hockey_federation.service + done + + echo "adaptive_hockey_federation.service is active" + + docker exec adaptive_hockey_federation python manage.py collectstatic --noinput + docker exec adaptive_hockey_federation python manage.py migrate diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml new file mode 100644 index 00000000..f08deb26 --- /dev/null +++ b/.github/workflows/pytest.yaml @@ -0,0 +1,32 @@ +name: Pytest + +on: + push: + branches: + - dev + +jobs: + pytest: + runs-on: ubuntu-latest + name: pytest + steps: + - name: Установка Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Установка Poetry + uses: snok/install-poetry@v1 + with: + poetry-version: 1.5.0 + + - name: Извлечение репозитория + uses: actions/checkout@v4 + + - name: Установка зависимостей + run: | + poetry install + - name: pytest + run: | + poetry run pytest + working-directory: adaptive_hockey_federation diff --git a/.gitignore b/.gitignore index 68bc17f9..944c5d95 100644 --- a/.gitignore +++ b/.gitignore @@ -85,7 +85,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -138,10 +138,8 @@ venv.bak/ # mkdocs documentation /site -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json +# ruff +.ruff_cache/ # Pyre type checker .pyre/ @@ -153,8 +151,36 @@ dmypy.json cython_debug/ # PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ + +# VScode +.vscode/ + +# Pylint settings +.pylintrc + +/adaptive_hockey_federation/parser/Именная заявка/ +resourses/ + + +#Django static +static/ +.DS_Store + +#Data JSON +*.json +*.zip + +#Data XLSX +*.XLSX +data/ + +#Data .csv +csv/ + +#MediaFiles +media/ + +# DS server +a_hockey-main/ +adaptive_hockey_federation/service/test_video/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..b60491fa --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-docstring-first + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.8 + hooks: + - id: ruff + exclude: migrations/|config/|tests/|.*settings(\.py|/)? + types_or: [ python ] + - id: ruff-format + exclude: migrations/|config/|tests/|.*settings(\.py|/)? + types_or: [ python ] + + - repo: local + hooks: + - id: export-dev-dependencies + name: Export dev Dependencies + language: system + pass_filenames: false + entry: poetry export --without-hashes --with dev --output requirements/develop.txt + files: ^(pyproject.toml|poetry.lock)$ + - id: export-prod-dependencies + name: Export prod Dependencies + language: system + pass_filenames: false + entry: poetry export --without-hashes --output requirements/production.txt + files: ^(pyproject.toml|poetry.lock)$ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..83527318 --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +# Определение переменных. +PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +MANAGE_DIR := $(PROJECT_DIR)/adaptive_hockey_federation/manage.py +DJANGO_DIR := $(PROJECT_DIR)/adaptive_hockey_federation +POETRY_RUN := poetry run python +DJANGO_RUN := $(POETRY_RUN) $(MANAGE_DIR) +DEV_DOCK_FILE := $(PROJECT_DIR)/infra/dev/docker-compose.dev.yaml +DS_DOCK_FILE := $(PROJECT_DIR)/a_hockey-main/app/ +SHELL_GREEN = \033[32m +SHELL_YELLOW = \033[33m +SHELL_NC := \e[0m + + +# Команда выполняемая по умолчанию. +.DEFAULT_GOAL := help + + +# Вызов документации. +help: + @echo "$(SHELL_YELLOW)Список полезных функций:$(SHELL_NC)." + @echo " init-app - $(SHELL_GREEN)Команда для автоустановки статики, миграций и регистрации супер-юзера.$(SHELL_NC)." + @echo " makemigrations - $(SHELL_GREEN)Команда для создания новых миграций и пременений их к базе данных.$(SHELL_NC)." + @echo " collectstatic - $(SHELL_GREEN)Команда для сбора статики.$(SHELL_NC)." + @echo " migrate - $(SHELL_GREEN)Команда для применения к базе данных готовых миграций.$(SHELL_NC)." + @echo " createsuperuser - $(SHELL_GREEN)Команда для создания супер-юзера.$(SHELL_NC)." + @echo " start-db - $(SHELL_GREEN)Команда для запуска локального контейнера postgres.$(SHELL_NC)." + @echo " stop-db - $(SHELL_GREEN)Команда для остановки локального контейнера postgres.$(SHELL_NC)." + @echo " clear-db - $(SHELL_GREEN)Команда для очистки volume локального контейнера postgres.$(SHELL_NC)." + @echo " run - $(SHELL_GREEN)Команда для локального запуска проекта.$(SHELL_NC)." + @echo " fill-db - $(SHELL_GREEN)Команда для заполнения базы данных реальными данными из json фикстур.$(SHELL_NC)." + @echo " fill-test-db - $(SHELL_GREEN)Команда для заполнения базы данных тестовыми данными при помощи фабрик генерации данных.$(SHELL_NC)." + @echo " export-db - $(SHELL_GREEN)Команда для экспорта данных из БД в JSON файл.$(SHELL_NC)." + @echo " import-db - $(SHELL_GREEN)Команда для импорта данных из JSON файла в БД.$(SHELL_NC)." + @echo " pytest - $(SHELL_GREEN)Команда для прогона юнит тестов pytest.$(SHELL_NC)." + @echo " shell - $(SHELL_GREEN)Команда для запуска Django-shell_plus.$(SHELL_NC)." + @echo " ds-mock - $(SHELL_GREEN)Команда для запуска имитации DS сервера (порт 8010).$(SHELL_NC)." + @echo " help - $(SHELL_GREEN)Команда вызова справки.$(SHELL_NC)." + @echo "$(SHELL_YELLOW)Для запуска исполнения команд используйте данные ключи совместно с командой 'make', например 'make init-app'." + @echo "При запуске команды 'make' без aкакого либо ключа, происходит вызов справки.$(SHELL_NC)." + + +# Подготовка проекта к локальному запуску. +init-app: collectstatic migrate createsuperuser + + +# Сбор статических файлов проекта. +collectstatic: + cd $(PROJECT_DIR) && $(DJANGO_RUN) collectstatic --no-input + + +# Создание новых миграций на основе сформированных моделей и пременение их к базе данных. +makemigrations: migrate + cd $(PROJECT_DIR) && $(DJANGO_RUN) makemigrations --no-input + + +# Применение собранных миграций к базе данных, на основе сформированных моделей. +migrate: + cd $(PROJECT_DIR) && $(DJANGO_RUN) migrate --no-input + + +# Создание супер-юзера. +createsuperuser: + cd $(PROJECT_DIR) && $(DJANGO_RUN) createsuperuser --no-input + + +# Запуск локального контейнера Postgres. +start-db: + docker-compose -f $(DEV_DOCK_FILE) up -d; \ + if [ $$? -ne 0 ]; \ + then \ + docker compose -f $(DEV_DOCK_FILE) up -d; \ + fi + +# Остановка контейнера Postgres. +stop-db: + docker-compose -f $(DEV_DOCK_FILE) down; \ + if [ $$? -ne 0 ]; \ + then \ + docker compose -f $(DEV_DOCK_FILE) down; \ + fi + +# Очистка БД Postgres. +clear-db: + docker-compose -f $(DEV_DOCK_FILE) down --volumes; \ + if [ $$? -ne 0 ]; \ + then \ + docker compose -f $(DEV_DOCK_FILE) down --volumes; \ + fi + + +# Локальный запуск сервера разработки и Celery. +run: + ( \ + trap 'kill 0' EXIT; \ + cd $(PROJECT_DIR) && $(DJANGO_RUN) runserver \ + ) + + +# Запуск django shell. +shell: + cd $(PROJECT_DIR) && $(DJANGO_RUN) shell_plus --plain + + +# Заполнение базы данных с помощью парсера. +fill-db: + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-db --fixtures + +#Заполнение базы данных фикстурами. +fill-test-db: + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --users + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --diagnosis --amount 8 + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --team --amount 20 + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --staffteam + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --player --amount 300 + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --document + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --competition --amount 10 + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --unload + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --game --amount 10 + cd $(PROJECT_DIR) && $(DJANGO_RUN) fill-test-db --json-player-data --amount 10 + + +# Экспорт данных из бд +export-db: + cd $(DJANGO_DIR) && $(DJANGO_RUN) export-db + + +# Импорт данных в бд +import-db: + cd $(DJANGO_DIR) && $(DJANGO_RUN) import-db + + +# Прогон тестов с помощью pytest. +pytest: + cd $(DJANGO_DIR) && pytest + + +# Локальный запуск сервера разработки и Celery. +ds-mock: + cd $(DJANGO_DIR)/service/mock_ds_server && fastapi dev --port 8010 main.py + + +.PHONY: help diff --git a/README.md b/README.md index b88abc34..5894b32f 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,775 @@ + # Федерация Адаптивного Хоккея -## Описание -Проект для [Федерации адаптивного хоккея](https://paraicehockey.ru/) -## Технологии -- [Python 3.11](https://www.python.org/downloads/release/python-3110/) -- [Poetry](https://python-poetry.org/) -- [Django](https://www.djangoproject.com/) -- [Nginx](https://nginx.org/) -- [Grafana](https://grafana.com/grafana/) -## Подготовка к запуску + +#### Описание + +Проект для [Федерации адаптивного хоккея](https://paraicehockey.ru/) + + + +# Содержание + + + + +1. [БРИФ](https://docs.google.com/document/d/1iHkA4Al-H-ppALPJDLMhb_Dl-ciRHH2npM6YRa2HQwg/edit) + + +1.1. [Инструкции и ритуалы на проекте](docs/materials/instructions.md) + + + +1.2. [ER - диаграмма сущностей](docs/ER_Diagram.drawio.jpg) + + + +1.3. [Референс](https://www.youtube.com/watch?v=b0LMWiSynQs) + + + +1.4. [Дизайн](https://www.figma.com/file/8uPoOvMuuJ8hMKASk7hog2/Hokei?type=design&node-id=0-1&mode=design) + + + +1.5. [Диаграмма обработки видео игроков](docs/diagram_video_workers.bpmn.svg) + + + +1.6. [Спецификация требований от аналитиков](docs/specification_of_analysts.md) + + + +2. [О проекте](#project) + + + +2.1. [Структура проекта](#structure) + + + +2.2. [Используемых технологий в проекте](#technologies-project) + + + +3. [Подготовка к запуску](#start) + + + +3.1. [Правила работы с git](#git) + + + +3.2. [Настройка poetry](#poetry) + + + +3.3. [Настройка pre-commit](#pre-commit) + + + +3.4. [Настройка переменных окружения](#env) + + + +4. [Запуск приложения](#run-app) + + + +4.1. [Запуск проекта локально](#run-local) + + + +4.2. [Запуск в Docker](#run-docker) + + + +4.3. [Запуск Celery локально](#start_celery_local) + + + +5. [Требования к тестам](#test-app) + + + +6. [Работа с API](#api) + + +7. [Работа с Celery](#celery) + + +8. [Имитация сервера DS (временный раздел)](#ds) + + +

+ + + +# 2. О проекте + + + +## 2.1 Структура проекта + + + +| Имя | Описание | + +| ------------- | ------------- | + +| infrastructure | Docker-compose файлы для запуска проекта с помощью Docker | + +| adaptive_hockey_federation | основной код приложения | + + + +## 2.2 Используемые технологии в проекте: + + + +[![Python][Python-badge]][Python-url] + +[![Poetry][Poetry-badge]][Poetry-url] + +[![Pre-commit][Pre-commit-badge]][Pre-commit-url] + +[![Django][Django-badge]][Django-url] + +[![Docker][Docker-badge]][Docker-url] + +[![Postgres][Postgres-badge]][Postgres-url] + +[![Celery][Celery-badge]][Celery-url] + + + +# 3. Подготовка к запуску + + + +Примечание: для работы над проектом необходим Python не ниже версии 3.11. + +Также необходимо установить Poetry (не ниже 1.5.0) и pre-commit. + + + +## 3.1. Правила работы с git (как делать коммиты и pull request-ы): + + + +1. Две основные ветки: `master` и `dev` + +2. Ветка `dev` — “предрелизная”. Т.е. здесь должен быть рабочий и выверенный код + +3. Создавая новую ветку, наследуйтесь от ветки `dev` + +4. В `master` находится только production-ready код (CI/CD) + +5. Правила именования веток + +- весь новый функционал — `feature/название-функционала` + +- исправление ошибок — `bugfix/название-багфикса` + +6. Пушим свою ветку в репозиторий и открываем Pull Request + +7. ВАЖНО! К таске из Projects привязываем свой Pull Request + + + +## 3.2. Poetry (инструмент для работы с виртуальным окружением и сборки пакетов): + + + + +Poetry - это инструмент для управления зависимостями и виртуальными окружениями, также может использоваться для сборки пакетов. В этом проекте Poetry необходим для дальнейшей разработки приложения, его установка обязательна.
+ + + +
+ + + +Как скачать и установить? + + + + + +### Установка: + + + +Установите poetry, не ниже версии 1.5.0 следуя [инструкции с официального сайта](https://python-poetry.org/docs/#installation). + +
+ + + +Команды для установки: + + + + + +Если у Вас уже установлен менеджер пакетов pip, то можно установить командой: + + +```bash +> *pip install poetry==1.5.0* +``` + + + +Если по каким-то причинам через pip не устанавливается, + +то для UNIX-систем и Bash on Windows вводим в консоль следующую команду: + + + +```bash +> *curl -sSL https://install.python-poetry.org | python -* +``` + + + +Для WINDOWS PowerShell: + + + +```pwsh +> *(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -* +``` + + + +
+ +
+ +После установки перезапустите оболочку и введите команду + + + +```bash +> poetry --version +``` + + + +Если установка прошла успешно, вы получите ответ в формате + + + +> Poetry (version 1.5.0) + + + +P.S.: Если при попытке проверить версию возникает ошибка об отсутствии исполняемого файла + +(poetry), необходимо после установки добавить его в Path Вашей системы + +(пути указаны по ссылке на официальную инструкцию по установке чуть выше.) + + + +Для дальнейшей работы введите команду: + + + +```bash +> poetry config virtualenvs.in-project true +``` + + + +Выполнение данной команды необходимо для создания виртуального окружения в + +папке проекта. + + + +После предыдущей команды создаём виртуальное окружение нашего проекта с + +помощью команды: + + + +```bash +> poetry install +``` + + + +Результатом выполнения команды станет создание в корне проекта папки .venv. + +Зависимости для создания окружения берутся из файлов poetry.lock (приоритетнее) + +и pyproject.toml + + + +Для добавления новой зависимости в окружение необходимо выполнить команду + + + +```bash +> poetry add +``` + + + +_Пример использования:_ + + + +```bash +> poetry add starlette +``` + + + +Также poetry позволяет разделять зависимости необходимые для разработки, от + +основных. + +Для добавления зависимости необходимой для разработки и тестирования необходимо + +добавить флаг ***--dev*** + + + +```bash +> poetry add --dev +``` + + + +_Пример использования:_ + + + +```bash +> poetry add pytest --dev +``` + + + +
+ + + +
+ + + +Порядок работы после настройки + + + + + +
+ + + +Чтобы активировать виртуальное окружение, введите команду: + + + +```bash +> poetry shell +``` + + + +Существует возможность запуска скриптов и команд с помощью команды без + +активации окружения: + + + +```bash +> poetry run .py +``` + + + +_Примеры:_ + + + +```bash +> poetry run python script_name>.py + +> + +> poetry run pytest + +> + +> poetry run black +``` + + + +Порядок работы в оболочке не меняется. Пример команды для Win: + + + +```bash +> python src\run_bot.py +``` + + + +Доступен стандартный метод работы с активацией окружения в терминале с помощью команд: + + + +Для WINDOWS: + + + +```pwsh +> source .venv/Scripts/activate +``` + + + +Для UNIX: + + + +```bash +> source .venv/bin/activate +``` + + + +
+ + + +В этом разделе представлены наиболее часто используемые команды. + +Подробнее: https://python-poetry.org/docs/cli/ + + + +#### Активировать виртуальное окружение + +```bash + +poetry shell + +``` + + + +#### Добавить зависимость + +```bash + +poetry add + +``` + + + +#### Обновить зависимости + +```bash + +poetry update + +``` + +## 3.3. Pre-commit (инструмент автоматического запуска различных проверок перед выполнением коммита): + + + +
+ + + +Настройка pre-commit + + + +
+ +1. Убедиться, что pre-comit установлен: + + + +```bash + +pre-commit --version + +``` + +2. Настроить git hook скрипт: + + + +```bash + +pre-commit install + +``` + + + +Далее при каждом коммите у вас будет происходить автоматическая проверка +линтером, а так же будет происходить автоматическое приведение к единому стилю. + +
+ + + +## 3.4. Настройка переменных окружения + + + +Перед запуском проекта необходимо создать копию файла + +```.env.example```, назвав его ```.env``` и установить значение токена бота, базы данных почты и тд. + + + +# 4. Запуск приложения + + ##### Клонировать репозиторий -```shell -git clone https://github.com/Studio-Yandex-Practicum/adaptive_hockey_federation.git + + +```bash + +git clone https://github.com/Studio-Yandex-Practicum/adaptive_hockey_federation.git + ``` + + ##### Перейти в директорию -```shell -cd adaptive_hockey_federation + + +```bash + +cd adaptive_hockey_federation + +``` + + + +## 4.1. Запуск проекта локально +Для удобного пользования проектом на локальном компьютере, реализованы короткие make команды. +1. После клонирования проекта перейдите в корневую директорию проекта при помощи консоли. +```bash +cd adaptive_hockey_federation +``` +2. Запустить локально контейнер postgres (если не запущен) +```bash +make start-db +``` +3. Для быстрого развёртывания проекта воспользуйтесь командой: +```bash +make init-app +``` +Скрипт сам соберёт статику, применит к базе готовые миграции и инициализирует создание супер-юзера, вам только понадобится ввести его данные. +4. Для запуска локального сервера используйте команду: +```bash +make run +``` +5. Если в модели были внесены изменения воспользуйтесь командой: +```bash +make makemigrations +``` +Будут созданы свежие миграции и сразу применены к базе данных. + +Более подробно со всеми возможностями можно ознакомится при помощи команды help: +```bash +make help +``` + + +## 4.2. Запуск проекта в Docker + + + +Собрать образ и запустить приложение из Dockerfile + + + +```bash + +docker build -t adaptive-hockey-federation . + +docker run --name adaptive-hockey-federation -it -p 8000:8000 adaptive-hockey-federation + +``` + + + +Собрать приложения в контейнеры при помощи Docker-compose: + + + +```bash + +docker-compose up -d --build + ``` -##### Установить зависимости -```shell -poetry install --with dev --with test +Django-проект и Nginx запустятся в контейнерах, при помощи инструкций в entrypoint.sh через 10 секунд добавится статика + + +# 5. Требования к тестам + + +#### Запуск тестов + +Все тесты запускаются командой: + +```bash + +pytest + ``` -## Локальный запуск -### **TODO** \ No newline at end of file +Или + + +```bash + +make pytest + +``` + + +Выборочно тесты запускаются с указанием выбранного файла: + +```bash + +pytest test_start.py + +``` + + +#### Написание тестов + +Для написания тестов используется pytest. + +Фикстуры хранятся в файле tests/conftest.py + +Основные тесты хранятся в директории tests. + +В зависимости от функционала тестов можно добавлять файлы тестов. + +Файлы тестов должны начинаться с "test_". + + + +#### Что необходимо тестировать + +Разработчик самостоятельно определяет функционал, который будет покрыт + +данными. Но, как правило, рекомендуется тестировать все написанные + +самостоятельно основные вьюхи, функции отправки и получения сообщений, + +функции перенаправления на сторонние или внутренние ресурсы. + + + +#### Рекомендации к написанию кода [Codestyle](docs/codestyle.md) + + + +# 6. Работа с API + + + +#### Доступ к документации API осуществляется по ссылкке (локальный доступ): + +Раздел будет обновляться. + +```bash +http://127.0.0.1:8000/api/docs/ +``` +Для корректного выполнения запросов из Swagger необходимов нажать кнопку ```Authorize``` + +и ввести API ключ. + +При выполнении прямых запросов к эндпоинтам, необходимо добавить в заголовок запроса + +ключ ```X-API-KEY``` со значением API ключа. + +API ключ вынесен в ```.env.example``` файл. Для удобства работы в локальной среде + +установлено значение по умолчанию. + + + +# 7. Имитация сервера DS (временный раздел) + +Для запуска имитации сервера DS: + +```commandline +make ds-mock +``` + +Сервер запустится по адресу 127.0.0.1:8010 + +Задержку "распознавания" можно изменить в константе DELAY в файле service/tasks.py. + + + + + + +[Python-url]: https://www.python.org/downloads/release/python-3110/ + +[Python-badge]: https://img.shields.io/badge/python-v3.11-yellow?style=for-the-badge&logo=python + + + +[Poetry-url]: https://python-poetry.org/ + +[Poetry-badge]: https://img.shields.io/badge/poetry-blue?style=for-the-badge&logo=poetry + + + +[Pre-commit-url]: https://pre-commit.com/ + +[Pre-commit-badge]: https://img.shields.io/badge/Pre--commit-teal?style=for-the-badge&logo=precommit + + + +[Django-url]: https://docs.djangoproject.com/en/4.2/releases/4.2.6/ + +[Django-badge]: https://img.shields.io/badge/Django-v4.2.6-008000?logo=django&style=for-the-badge + + + +[Docker-url]: https://www.docker.com/ + +[Docker-badge]: https://img.shields.io/badge/docker-red?style=for-the-badge&logo=docker + + + +[Postgres-url]: https://www.postgresql.org/ + +[Postgres-badge]: https://img.shields.io/badge/postgresql-gray?style=for-the-badge&logo=postgresql + + +[Celery-url]: https://docs.celeryq.dev/en/stable/ + +[Celery-badge]: https://img.shields.io/badge/Celery-blue?style=for-the-badge&logo=circleci diff --git a/adaptive_hockey_federation/analytics/__init__.py b/adaptive_hockey_federation/analytics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/analytics/apps.py b/adaptive_hockey_federation/analytics/apps.py new file mode 100644 index 00000000..77947f83 --- /dev/null +++ b/adaptive_hockey_federation/analytics/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class AnalyticsConfig(AppConfig): + """Класс-конфигуратор для приложения analytics.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "analytics" diff --git a/adaptive_hockey_federation/analytics/forms.py b/adaptive_hockey_federation/analytics/forms.py new file mode 100644 index 00000000..eafc4f29 --- /dev/null +++ b/adaptive_hockey_federation/analytics/forms.py @@ -0,0 +1,78 @@ +import datetime + +from django import forms +from django.db.models.functions import ExtractYear +from main.models import City, DisciplineName, Player + + +class AnalyticsFilterForm(forms.Form): + """Класс-форма для фильтрации страницы с аналитикой.""" + + birthday = forms.ModelChoiceField( + queryset=Player.objects.dates("birthday", "year").values_list( + ExtractYear("birthday"), + flat=True, + ), + required=False, + label="Год рождения", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + empty_label="Все", + ) + timespan = forms.ChoiceField( + choices=[], + required=False, + label="За время", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + ) + city = forms.ModelChoiceField( + queryset=City.objects.all(), + required=False, + label="Город", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + empty_label="Все", + ) + discipline = forms.ModelChoiceField( + queryset=DisciplineName.objects.all(), + required=False, + label="Дисциплина", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + empty_label="Все", + ) + + class Meta: + fields = ("birthday", "timespan", "city", "discipline") + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + Вызывает метод, добавляющий в поле timespan выбор + временного интервала для фильтрации. + """ + super().__init__(*args, **kwargs) + self.set_timespan_choices() + + def set_timespan_choices(self): + """ + Метод, добавляющий выбор временного интервала для фильтрации. + + 1. За все время + 2. За текущий месяц + 3. За текущий год. + """ + self.fields["timespan"].choices = ( + (None, "Все"), + ( + datetime.date.today().replace( + day=1, + ), + "Текущий месяц", + ), + ( + datetime.date.today().replace( + month=1, + day=1, + ), + "Текущий год", + ), + ) diff --git a/adaptive_hockey_federation/analytics/schema.py b/adaptive_hockey_federation/analytics/schema.py new file mode 100644 index 00000000..8dc22308 --- /dev/null +++ b/adaptive_hockey_federation/analytics/schema.py @@ -0,0 +1,6 @@ +ANALYTICS_SEARCH_FIELDS: dict = { + "timespan": "addition_date__gte", + "birthday": "birthday__year__exact", + "discipline": "discipline_name__id", + "city": "team__city", +} diff --git a/adaptive_hockey_federation/analytics/urls.py b/adaptive_hockey_federation/analytics/urls.py new file mode 100644 index 00000000..e3e632fa --- /dev/null +++ b/adaptive_hockey_federation/analytics/urls.py @@ -0,0 +1,12 @@ +from analytics import views +from django.urls import include, path + +app_name = "analytics" + +analytics_urlpatterns = [ + path("", views.AnalyticsListView.as_view(), name="analytics"), +] + +urlpatterns = [ + path("analytics/", include(analytics_urlpatterns)), +] diff --git a/adaptive_hockey_federation/analytics/views.py b/adaptive_hockey_federation/analytics/views.py new file mode 100644 index 00000000..9ad9f3ae --- /dev/null +++ b/adaptive_hockey_federation/analytics/views.py @@ -0,0 +1,98 @@ +from datetime import datetime + +from analytics.forms import AnalyticsFilterForm +from core.constants import GENDER_CHOICES +from core.permissions import AdminRequiredMixin +from dateutil.relativedelta import relativedelta +from main.controllers.player_views import PlayersListView +from main.models import Nosology, Player, Team +from unloads.utils import model_get_queryset + + +class AnalyticsListView( + AdminRequiredMixin, + PlayersListView, +): + """View-класс для отображения страницы с аналитикой.""" + + template_name = "analytics/analytics.html" + + def get_queryset(self): + """Метод для получения QuerySet с заданными параметрами.""" + queryset = super().get_queryset() + dict_param = dict(self.request.GET) + dict_param = {k: v for k, v in dict_param.items() if v != [""]} + if len(dict_param) > 0: + queryset = model_get_queryset( + "analytics", + Player, + dict_param, + queryset, + ) + return ( + queryset.select_related("diagnosis") + .select_related("discipline_name") + .order_by("surname") + ) + + def get_context_data(self, *, object_list=None, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + date_18_years_ago = datetime.now() - relativedelta(years=18) + context["form"] = AnalyticsFilterForm(self.request.GET or None) + context["dashboard"] = { + "primary": [ + ("игроков", self.get_queryset().count()), + ( + "команд", + teams_count := Team.objects.filter( + id__in=self.get_queryset() + .values_list("team", flat=True) + .distinct(), + ).count(), + ), + ("городов", teams_count), + ], + "secondary": [ + ( + "мальчиков", + self.get_queryset() + .filter(gender=GENDER_CHOICES[0][1]) + .count(), + ), + ( + "девочек", + self.get_queryset() + .filter(gender=GENDER_CHOICES[1][1]) + .count(), + ), + ( + "младше 18", + self.get_queryset() + .filter(birthday__gte=date_18_years_ago) + .count(), + ), + ( + "старше 18", + self.get_queryset() + .filter(birthday__lt=date_18_years_ago) + .count(), + ), + ], + "nosology": [ + ( + f"{nosology.name}", + self.get_queryset() + .filter(diagnosis__nosology=nosology) + .count(), + ) + for i, nosology in enumerate( + Nosology.objects.filter( + diagnosis__in=self.get_queryset() + .values_list("diagnosis", flat=True) + .distinct(), + ).distinct(), + ) + ], + } + return context diff --git a/adaptive_hockey_federation/competitions/__init__.py b/adaptive_hockey_federation/competitions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/competitions/admin.py b/adaptive_hockey_federation/competitions/admin.py new file mode 100644 index 00000000..17b80423 --- /dev/null +++ b/adaptive_hockey_federation/competitions/admin.py @@ -0,0 +1,16 @@ +from competitions.models import Competition +from django.contrib import admin + + +class CompetitionAdmin(admin.ModelAdmin): + """Модель соревнований для административной панели Django.""" + + list_display = ( + "pk", + "title", + ) + search_fields = ("title",) + ordering = ["title"] + + +admin.site.register(Competition, CompetitionAdmin) diff --git a/adaptive_hockey_federation/competitions/apps.py b/adaptive_hockey_federation/competitions/apps.py new file mode 100644 index 00000000..1ad9f883 --- /dev/null +++ b/adaptive_hockey_federation/competitions/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class CompetitionsConfig(AppConfig): + """Класс-конфигуратор для приложения competitions.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "competitions" + verbose_name = "Соревнования" diff --git a/adaptive_hockey_federation/competitions/forms.py b/adaptive_hockey_federation/competitions/forms.py new file mode 100644 index 00000000..cdca46a1 --- /dev/null +++ b/adaptive_hockey_federation/competitions/forms.py @@ -0,0 +1,241 @@ +from typing import Any + +from competitions.models import CHAR_FIELD_LENGTH, Competition +from competitions.validators import date_not_before_today +from core.constants import FORM_HELP_TEXTS +from django import forms +from django.core.exceptions import ValidationError +from django.forms import ModelMultipleChoiceField +from main.forms import CityChoiceField, CustomMultipleChoiceField +from main.models import DisciplineName, Team + + +class CustomDisciplineMultipleChoiceField(ModelMultipleChoiceField): + """Множественное поле выбора для дисциплин.""" + + def _check_values(self, value): + try: + value = frozenset(value) + except TypeError: + raise ValidationError("Неверный список дисциплин!") + value = list(value) + qs = DisciplineName.objects.filter(pk__in=value) + return qs + + +class CompetitionForm(forms.ModelForm): + """Форма для соревнований.""" + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + 1. Вызывает метод, делающий поля неактивными при соблюдении + условия + 2. Включает валидацию полей, если они активны. + """ + super(CompetitionForm, self).__init__(*args, **kwargs) + if self.instance and self.instance.id: + self.set_readonly("date_start", self.instance.is_started) + self.set_readonly("date_end", self.instance.is_ended) + + for field in [self.fields["date_start"], self.fields["date_end"]]: + if field.disabled: + field.validators = [] + else: + field.validators = [date_not_before_today] + + title = forms.CharField(label="Название") + city = CityChoiceField(label="Город, где проводятся соревнования") + + available_disciplines = ModelMultipleChoiceField( + queryset=DisciplineName.objects.all().order_by("name"), + required=False, + help_text=FORM_HELP_TEXTS["disciplines"], + label="Дисциплины", + ) + + disciplines = CustomMultipleChoiceField( + required=True, + help_text=FORM_HELP_TEXTS["disciplines"], + label="Дисциплины", + ) + + location = forms.CharField( + label="Место проведения (адрес, учреждение и т.д.)", + max_length=CHAR_FIELD_LENGTH, + ) + + date_start = forms.DateField( + widget=forms.DateInput( + attrs={"type": "date", "class": "form-control"}, + ), + label="Дата начала", + error_messages={"required": "Пожалуйста, укажите дату начала."}, + ) + date_end = forms.DateField( + widget=forms.DateInput( + attrs={"type": "date", "class": "form-control"}, + ), + label="Дата завершения", + error_messages={"required": "Пожалуйста, укажите дату завершения."}, + ) + + def set_readonly(self, field: str, readonly_value: bool): + """ + Делает поле формы неактивным при соблюдении условия. + + - field - название поля. + - readonly_value - любое bool условие. Если условие не + соблюдается, поле активируется. + """ + self.fields[field].disabled = readonly_value + + class Meta: + model = Competition + fields = [ + "title", + "city", + "location", + "available_disciplines", + "disciplines", + "date_start", + "date_end", + ] + + def save_m2m(self): + """Метод сохраняет M2M связи между соревнованиями и дисциплинами.""" + self.instance.disciplines.through.objects.filter( + disciplinename__in=self.cleaned_data["disciplines"], + ).delete() + self.instance.disciplines.set(self.cleaned_data["disciplines"]) + + def save(self, commit=True): + """Метод создает и сохраняет объект соревнования в базе данных.""" + instance = super(CompetitionForm, self).save(commit=True) + if commit: + instance.save() + self.save_m2m() + return instance + + def clean(self): + """Метод, выполняющий валидацию полей формы.""" + cleaned_data = super().clean() + date_start = cleaned_data.get("date_start") + date_end = cleaned_data.get("date_end") + if date_start and date_end and date_start > date_end: + raise forms.ValidationError( + "Дата окончания соревнования должна " + "быть позже или совпадать с датой " + "начала.", + ) + return cleaned_data + + +class TeamField(forms.ModelChoiceField): + """ + Заказное поле для выбора названия команды. + + Работает с виджетом TextInput. + Для корректного отображения на вэб-странице должен быть элемент: + + + ...и так для каждой команды в списке. + . + """ + + def __init__(self, competition: Competition): + """ + Метод инициализации экземпляра класса. + + 1. Добавляет виджет для поиска команды по названию + 2. Добавляет атрибут competition. + """ + super(TeamField, self).__init__( + queryset=Team.objects.all(), + widget=forms.TextInput( + attrs={ + "class": "form-control", + "list": "available_teams", + "placeholder": ( + "Начните вводить название команды " + "и выберите из списка" + ), + }, + ), + label="Поиск команды для допуска", + ) + self.competition = competition + + def clean(self, value: Any) -> Any: + """Переопределенный метод родительского класса.""" + if not isinstance(value, str) or value in self.empty_values: + raise ValidationError(self.error_messages["required"]) + + value = value.strip() + + if team := Team.get_by_name(value): + if self.competition.teams.filter(id=team.id).exists(): + raise ValidationError("Команда уже добавлена в соревнования.") + return super().clean(team) + + raise ValidationError("Такой команды не существует") + + +class CompetitionUpdateForm(CompetitionForm): + """Форма для обновления соревнований.""" + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + 1. Добавляет множественное поле выбора для disciplines + 2. Добавляет множественное поле выбора для available_disciplines. + """ + super(CompetitionForm, self).__init__(*args, **kwargs) + if queryset := self.instance.disciplines.all(): + self.fields["disciplines"] = CustomDisciplineMultipleChoiceField( + queryset=queryset, + required=True, + help_text=FORM_HELP_TEXTS["disciplines"], + label="Дисциплины", + ) + queryset_available = DisciplineName.objects.all().difference(queryset) + self.fields["available_disciplines"] = ( + CustomDisciplineMultipleChoiceField( + queryset=queryset_available, + required=False, + help_text=FORM_HELP_TEXTS["available_disciplines"], + label="Дисциплины", + ) + ) + + +class CompetitionTeamForm(forms.ModelForm): + """ + Форма для добавления команд в соревнование. + + Работает с промежуточной моделью Competition_Team. + """ + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + 1. Добавляет атрибут competition + 2. Добавляет поле выбора team. + """ + self.competition = kwargs.pop("competition") + super(CompetitionTeamForm, self).__init__(*args, **kwargs) + self.fields["team"] = TeamField(competition=self.competition) + + class Meta: + model = Competition.teams.through + fields = ["team"] + + def save(self, commit=True): + """Метод создает и сохраняет объект в базе данных.""" + instance = super(CompetitionTeamForm, self).save(commit=False) + if commit: + instance.save() + return instance diff --git a/adaptive_hockey_federation/competitions/migrations/0001_initial.py b/adaptive_hockey_federation/competitions/migrations/0001_initial.py new file mode 100644 index 00000000..bf47bfe4 --- /dev/null +++ b/adaptive_hockey_federation/competitions/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.13 on 2024-06-03 19:21 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('main', '__first__'), + ] + + operations = [ + migrations.CreateModel( + name='Competition', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=250)), + ('date_start', models.DateField()), + ('date_end', models.DateField()), + ('location', models.CharField(max_length=250)), + ('city', models.ForeignKey(help_text='Город проведения соревнований', on_delete=django.db.models.deletion.CASCADE, to='main.city', verbose_name='Город проведения соревнований')), + ('disciplines', models.ManyToManyField(help_text='Дисциплины', related_name='competitions', to='main.disciplinename', verbose_name='Дисциплины')), + ('teams', models.ManyToManyField(help_text='Состав команд участников', related_name='competition_teams', to='main.team', verbose_name='Состав команд участников')), + ], + options={ + 'verbose_name': 'Соревнование', + 'verbose_name_plural': 'Соревнования', + 'ordering': ('date_start',), + 'permissions': [('list_view_competition', 'Can view list of Соревнование'), ('list_team_competition', 'Can view list of Команда on Соревнование'), ('delete_team_competition', 'Can delete Команда from Соревнование')], + }, + ), + ] diff --git a/adaptive_hockey_federation/competitions/migrations/__init__.py b/adaptive_hockey_federation/competitions/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/competitions/models.py b/adaptive_hockey_federation/competitions/models.py new file mode 100644 index 00000000..d06a71f4 --- /dev/null +++ b/adaptive_hockey_federation/competitions/models.py @@ -0,0 +1,73 @@ +from competitions.utils import get_now_day, pluralize_days +from django.db import models +from django.utils.translation import gettext_lazy as _ +from main.models import City, DisciplineName, Team + +CHAR_FIELD_LENGTH = 250 + + +class Competition(models.Model): + """Модель соревнований.""" + + title = models.CharField(max_length=CHAR_FIELD_LENGTH) + date_start = models.DateField() + date_end = models.DateField() + city = models.ForeignKey( + City, + on_delete=models.CASCADE, + verbose_name=_("Город проведения соревнований"), + help_text=_("Город проведения соревнований"), + ) + location = models.CharField(max_length=CHAR_FIELD_LENGTH) + teams = models.ManyToManyField( + Team, + related_name="competition_teams", + verbose_name=_("Состав команд участников"), + help_text=_("Состав команд участников"), + ) + disciplines = models.ManyToManyField( + DisciplineName, + related_name=("competitions"), + verbose_name=_("Дисциплины"), + help_text=_("Дисциплины"), + ) + + class Meta: + verbose_name = "Соревнование" + verbose_name_plural = "Соревнования" + ordering = ("date_start",) + permissions = [ + ("list_view_competition", "Can view list of Соревнование"), + ( + "list_team_competition", + "Can view list of Команда on Соревнование", + ), + ( + "delete_team_competition", + "Can delete Команда from Соревнование", + ), + ] + + def __str__(self): + """Метод, использующий поле title для строкового представления.""" + return self.title + + def period_duration(self): + """Функция рассчитывает длительность турнира.""" + duration = self.date_end - self.date_start + return pluralize_days(duration.days + 1) + + @property + def is_in_process(self) -> bool: + """Возвращает True, если соревнование сейчас идет.""" + return self.date_start <= get_now_day() <= self.date_end + + @property + def is_started(self) -> bool: + """Проверяет, прошла ли дата начала соревнования.""" + return self.date_start < get_now_day() + + @property + def is_ended(self) -> bool: + """Проверяет, прошла ли дата окончания соревнования.""" + return self.date_end < get_now_day() diff --git a/adaptive_hockey_federation/competitions/schema.py b/adaptive_hockey_federation/competitions/schema.py new file mode 100644 index 00000000..0df77291 --- /dev/null +++ b/adaptive_hockey_federation/competitions/schema.py @@ -0,0 +1,43 @@ +from django.urls import reverse + + +def get_competitions_table_data(competitions): + table_data = [] + for competition in competitions: + table_data.append( + { + "pk": competition.pk, + "disciplines": ", ".join( + competition.disciplines.values_list("name", flat=True), + ), + "data": competition.date_start, + "data_end": competition.date_end, + "title": competition.title, + "city": competition.city, + "duration": competition.period_duration, + "is_active": competition.is_in_process, + "_ref_": { + "name": "Участники", + "type": "button", + "url": reverse( + "competitions:competition_id", + args=[competition.pk], + ), + }, + }, + ) + return table_data + + +def get_competitions_table_head(): + return { + "pk": "Nr.", + "disciplines": "Дисциплины", + "date": "Начало соревнований", + "date_end": "Конец соревнований", + "title": "Наименование", + "city": "Город", + "duration": "Длительность", + "is_active": "Активно", + "teams": "Участники", + } diff --git a/adaptive_hockey_federation/competitions/urls.py b/adaptive_hockey_federation/competitions/urls.py new file mode 100644 index 00000000..059fb003 --- /dev/null +++ b/adaptive_hockey_federation/competitions/urls.py @@ -0,0 +1,42 @@ +from competitions import views +from django.urls import include, path + +app_name = "competitions" + +competitions_urlpatterns = [ + path("", views.CompetitionListView.as_view(), name="competitions"), + path( + "create/", + views.CreateCompetitionView.as_view(), + name="competition_add", + ), + path( + "/edit/", + views.UpdateCompetitionView.as_view(), + name="competition_update", + ), + path( + "/delete/", + views.DeleteCompetitionView.as_view(), + name="competition_delete", + ), + path( + "/", + views.competition_team_manage_view, + name="competition_id", + ), + path( + "/teams//delete/", + views.DeleteTeamFromCompetition.as_view(), + name="competitions_id_delete", + ), + path( + "/teams//add/", + views.AddTeamToCompetition.as_view(), + name="competitions_id_add", + ), +] + +urlpatterns = [ + path("competitions/", include(competitions_urlpatterns)), +] diff --git a/adaptive_hockey_federation/competitions/utils.py b/adaptive_hockey_federation/competitions/utils.py new file mode 100644 index 00000000..bac1bc08 --- /dev/null +++ b/adaptive_hockey_federation/competitions/utils.py @@ -0,0 +1,20 @@ +import datetime as dt + + +def pluralize_days(days: int) -> str: + """Вспомогательная функция для коректного отображения дней.""" + if days % 10 == 1 and days % 100 != 11: + return f"{days} день" + elif ( + days % 10 >= 2 + and days % 10 <= 4 + and (days % 100 < 10 or days % 100 >= 20) + ): + return f"{days} дня" + else: + return f"{days} дней" + + +def get_now_day(): + """Возвращает сегодняшнее число в формате date.""" + return dt.datetime.date(dt.datetime.now()) diff --git a/adaptive_hockey_federation/competitions/validators.py b/adaptive_hockey_federation/competitions/validators.py new file mode 100644 index 00000000..2aa00fe1 --- /dev/null +++ b/adaptive_hockey_federation/competitions/validators.py @@ -0,0 +1,16 @@ +from datetime import datetime + +from django.core.exceptions import ValidationError + + +def date_not_before_today(value): + """ + Проверка на создание соревнования с датой начала в прошлом. + + Должен вызываться только при создании соревнования. + """ + if value < datetime.date(datetime.now()): + raise ValidationError( + "Введите дату не раньше сегодняшней.", + code="past_is_forbidden", + ) diff --git a/adaptive_hockey_federation/competitions/views.py b/adaptive_hockey_federation/competitions/views.py new file mode 100644 index 00000000..e253eb24 --- /dev/null +++ b/adaptive_hockey_federation/competitions/views.py @@ -0,0 +1,442 @@ +from competitions.forms import ( + CompetitionForm, + CompetitionTeamForm, + CompetitionUpdateForm, +) +from competitions.models import Competition, Team +from competitions.schema import ( + get_competitions_table_data, + get_competitions_table_head, +) +from django.contrib.auth.decorators import login_required, user_passes_test +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, + UserPassesTestMixin, +) +from django.contrib.auth.views import redirect_to_login +from django.core.exceptions import PermissionDenied +from django.db.models import Case, F, Q, QuerySet, Value, When +from django.db.models.functions import Now +from django.db.models.lookups import GreaterThan, LessThan +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse, reverse_lazy +from django.views import View +from django.views.generic import RedirectView +from django.views.generic.detail import SingleObjectMixin +from django.views.generic.edit import ( + CreateView, + DeleteView, + DeletionMixin, + UpdateView, +) +from django.views.generic.list import ListView +from main.controllers.team_views import CityListMixin +from main.controllers.utils import get_team_href +from users.utilits.send_mails import send_welcome_mail + + +class CompetitionListView( + LoginRequiredMixin, + UserPassesTestMixin, + ListView, +): + """Представление списка соревнований.""" + + model = Competition + template_name = "main/competitions/competitions.html" + context_object_name = "competitions" + paginate_by = 10 + ordering = ["id"] + + def test_func(self): + """Проверка, что пользователь является представителем команды.""" + user = self.request.user + return user.is_authenticated and ( + user.is_agent or user.is_superuser or user.is_admin + ) + + def get_permission_denied_message(self): + """Сообщение об отказе в доступе.""" + return "Отсутствует разрешение на просмотр списка соревнований." + + def handle_no_permission(self): + """Обработка ситуации, когда у пользователя нет прав.""" + if not self.request.user.is_authenticated: + return redirect_to_login( + self.request.get_full_path(), + self.get_login_url(), + self.get_redirect_field_name(), + ) + else: + raise PermissionDenied(self.get_permission_denied_message()) + + def get_queryset(self): + """Метод для получения QuerySet с заданными параметрами.""" + queryset = super().get_queryset() + search_params = self.request.GET.dict() + search_column = search_params.get("search_column") + search = search_params.get("search") + if search_column: + if search_column.lower() in ["все", "all"]: + queryset = queryset.filter( + Q(title__icontains=search) + | Q(city__name__icontains=search) + | Q(date_start__icontains=search) + | Q(date_end__icontains=search) + | Q(pk__icontains=search), + ) + elif search_column == "title": + queryset = queryset.filter(title__icontains=search) + elif search_column == "city": + queryset = queryset.filter(city__name__icontains=search) + elif search_column == "is_active": + queryset = queryset.annotate( + is_active_view=Case( + When( + LessThan(F("date_start"), Now()) + & GreaterThan(F("date_end"), Now()), + then=Value("true"), + ), + default=Value("false"), + ), + ).filter(is_active_view__icontains=search_params["active"]) + elif search_column == "teams": + queryset = queryset.filter(teams__name__icontains=search) + elif search_column == "date": + queryset = queryset.filter( + Q(date_start__year__icontains=search_params["year"]) + & Q( + date_start__month__icontains=search_params[ + "month" + ].lstrip("0"), + ) + & Q( + date_start__day__icontains=search_params["day"].lstrip( + "0", + ), + ), + ) + elif search_column == "date_end": + queryset = queryset.filter( + Q(date_end__year__icontains=search_params["year"]) + & Q( + date_end__month__icontains=search_params[ + "month" + ].lstrip("0"), + ) + & Q( + date_end__day__icontains=search_params["day"].lstrip( + "0", + ), + ), + ) + return queryset + + def get_context_data(self, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + competitions = context["competitions"] + context["page_title"] = "Редактирование соревнование" + context["table_head"] = get_competitions_table_head + context["table_data"] = get_competitions_table_data(competitions) + return context + + +class UpdateCompetitionView( + LoginRequiredMixin, + PermissionRequiredMixin, + UpdateView, + CityListMixin, +): + """Обновление информации о соревновании.""" + + model = Competition + form_class = CompetitionUpdateForm + template_name = "main/competitions/competition_create_edit.html" + permission_required = "competitions.change_competition" + permission_denied_message = ( + "Отсутствует разрешение на изменение карточки соревнований." + ) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse_lazy( + "competitions:competition_id", + kwargs={"pk": self.object.pk}, + ) + + def get_object(self, queryset=None): + """Получить объект по id или вызвать ошибку 404.""" + return get_object_or_404(Competition, id=self.kwargs["pk"]) + + def get_initial(self): + """Вернуть словарь initial с указанными атрибутами объекта.""" + initial = { + "city": self.object.city.name, + "date_start": self.object.date_start.isoformat(), + "date_end": self.object.date_end.isoformat(), + } + return initial + + def get_context_data(self, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + context["cities"] = self.get_cities() + context["page_title"] = "Редактирование соревнования" + return context + + +class DeleteCompetitionView( + LoginRequiredMixin, + PermissionRequiredMixin, + DeleteView, +): + """Удаление соревнований.""" + + object = Competition + model = Competition + success_url = reverse_lazy("competitions:competitions") + permission_required = "competitions.delete_competition" + permission_denied_message = ( + "Отсутствует разрешение на изменение карточки соревнований." + ) + + def get_object(self, queryset=None): + """Получить объект по id или вызвать ошибку 404.""" + return get_object_or_404(Competition, id=self.kwargs["pk"]) + + +class AddTeamToCompetition( + LoginRequiredMixin, + PermissionRequiredMixin, + RedirectView, +): + """ + Представление добавления команды в соревнования. + + В данном виде не отображает какой-то отдельной страницы либо формы. + Используется для обработки нажатия кнопки либо соответствующего + post-запроса. Добавляет команду в соревнование, после чего делает + редирект на страницу управления соответствующим + соревнованием. + """ + + pattern_name = "competitions:competition_id" + http_method_names = ("post",) + permission_required = "competitions.change_competition" + permission_denied_message = ( + "Отсутствует разрешение на изменение списка команд на соревновании." + ) + + def dispatch(self, request, *args, **kwargs): + """Обработать POST-запрос или вернуть на страницу соревнования.""" + if request.method.lower() == "post": + competition = get_object_or_404( + Competition, + id=kwargs["competition_id"], + ) + team = get_object_or_404(Team, id=kwargs["pk"]) + competition.teams.add(team) + if team.curator and team.curator.email: + send_welcome_mail( + team=team, + competition=competition, + curator_email=team.curator.email, + ) + return super(AddTeamToCompetition, self).dispatch( + request, + kwargs["competition_id"], + ) + + +class DeleteTeamFromCompetition( + LoginRequiredMixin, + PermissionRequiredMixin, + View, + DeletionMixin, + SingleObjectMixin, +): + """Удаление команд из участия в соревновании.""" + + object = Competition.teams.through + model = Competition.teams.through + permission_required = "competitions.change_competition" + permission_denied_message = ( + "Отсутствует разрешение на удаление команд с соревнования." + ) + + def get_object(self, queryset=None): + """Получить объект по атрибутам или вызвать ошибку 404.""" + team_in_competition = get_object_or_404( + Competition.teams.through, + competition=self.kwargs["competition_id"], + team=self.kwargs["pk"], + ) + return team_in_competition + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse_lazy( + "competitions:competition_id", + kwargs={"pk": self.kwargs["competition_id"]}, + ) + + +class CreateCompetitionView( + LoginRequiredMixin, + PermissionRequiredMixin, + CreateView, + CityListMixin, +): + """Представление создания соревнования.""" + + model = Competition + form_class = CompetitionForm + template_name = "main/competitions/competition_create_edit.html" + permission_required = "competitions.add_competition" + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse_lazy( + "competitions:competition_id", + kwargs={"pk": self.object.pk}, + ) + + def get_object(self, queryset=None): + """Получить объект по id или вызвать ошибку 404.""" + return get_object_or_404(Competition, id=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super(CreateCompetitionView, self).get_context_data(**kwargs) + context["cities"] = self.get_cities() + context["page_title"] = "Создать соревнование" + return context + + def post(self, request, *args, **kwargs): + """ + Обработать POST-запрос. + + 1. Создать экземпляр формы с переданными атрибутами + 2. Вызвать валидацию полей формы. + """ + self.disciplines = request.POST.get("disciplines", None) + return super().post(request, *args, **kwargs) + + +def check_permissions(user): + """ + Проверка прав пользователя. + + Доступ для: + 1. Представителя команды + 2. Администратора. + """ + return user.is_authenticated and ( + user.is_agent or user.is_superuser or user.is_admin + ) + + +@login_required() +@user_passes_test( + check_permissions, + login_url="login", + redirect_field_name=None, +) +def competition_team_manage_view(request, pk): + """ + Представление для управления соревнованием. + + - Отображает команды, участвующие в соревновании, доступные команды + (т.е. те, которые в соревновании не участвуют), + - Позволяет добавлять команды в соревнование и удалять их из него. + + Использована вью-функция вместо вью-класса в связи с необходимостью + более тонкой настройки формы для работы с промежуточной моделью. + """ + competition = get_object_or_404( + Competition.objects.prefetch_related("teams"), + id=pk, + ) + + def _get_table_data( + competition_instance: Competition, + team_queryset: QuerySet, + button_name: str | None = None, + button_url_name: str | None = None, + ): + data = [ + { + "id": team.id, + "name": get_team_href(team), + "city": team.city, + "discipline_name": team.discipline_name, + "action_button": { + "name": button_name, + "url": reverse( + button_url_name, + kwargs={ + "competition_id": competition_instance.id, + "pk": team.id, + }, + ), + }, + } + for team in team_queryset + ] + return data + + def get_context(): + """Внутренний метод получения контекста.""" + # Команды, участвующие в соревнованиях + teams_table_data = _get_table_data( + competition, + competition.teams.all(), + button_name="Отстранить", + button_url_name="competitions:competitions_id_delete", + ) + + # Команды, доступные для добавления к соревнованиям + available_teams = Team.objects.exclude(competition_teams=competition) + available_teams_table_data = _get_table_data( + competition, + available_teams, + button_name="Допустить", + button_url_name="competitions:competitions_id_add", + ) + + _context = { + "table_data": teams_table_data, + "available_table_data": available_teams_table_data, + "available_teams_list": list( + available_teams.values_list("name", flat=True), + ), + "object": competition, + } + return _context + + context = get_context() + + if request.method != "POST": + context["form"] = CompetitionTeamForm(competition=competition) + return render( + request, + "main/competitions_id/competitions_id.html", + context, + ) + + form = CompetitionTeamForm(request.POST, competition=competition) + context["form"] = form + + if not form.is_valid(): + return render( + request, + "main/competitions_id/competitions_id.html", + context, + ) + + competition_team = form.save(commit=False) + competition_team.competition = competition + competition_team.save() + return redirect("competitions:competition_id", competition.id) diff --git a/adaptive_hockey_federation/core/__init__.py b/adaptive_hockey_federation/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/apps.py b/adaptive_hockey_federation/core/apps.py new file mode 100644 index 00000000..9544f324 --- /dev/null +++ b/adaptive_hockey_federation/core/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + """Класс-конфигуратор для приложения core.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "core" diff --git a/adaptive_hockey_federation/core/asgi.py b/adaptive_hockey_federation/core/asgi.py new file mode 100644 index 00000000..dfb85307 --- /dev/null +++ b/adaptive_hockey_federation/core/asgi.py @@ -0,0 +1,10 @@ +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + "core.config.dev_settings", +) + +application = get_asgi_application() diff --git a/adaptive_hockey_federation/core/config/__init__.py b/adaptive_hockey_federation/core/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/config/base_settings.py b/adaptive_hockey_federation/core/config/base_settings.py new file mode 100644 index 00000000..c7302041 --- /dev/null +++ b/adaptive_hockey_federation/core/config/base_settings.py @@ -0,0 +1,172 @@ +from pathlib import Path + +import environ +from django.contrib.messages import constants as messages + +env = environ.Env() + +BASE_DIR = Path(__file__).resolve().parent.parent.parent + +SECRET_KEY = env("SECRET_KEY", default="django_secret_key") + +DEBUG = env("DEBUG", default=False) + +ALLOWED_HOSTS = env.list( + "ALLOWED_HOSTS", + default=["*"], +) + +CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=["https://127.0.0.1"]) + +DEFAULT_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", +] + +EXTERNAL_APPS = [ + "phonenumber_field", + "rest_framework", + "drf_yasg", +] + +LOCAL_APPS = [ + "main.apps.MainConfig", + "users.apps.UsersConfig", + "core.apps.CoreConfig", + "competitions.apps.CompetitionsConfig", + "analytics.apps.AnalyticsConfig", + "unloads.apps.UnloadsConfig", + "games.apps.GamesConfig", +] + +INSTALLED_APPS = EXTERNAL_APPS + DEFAULT_APPS + LOCAL_APPS + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "core.urls" + +TEMPLATES_DIR = BASE_DIR / "templates" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [TEMPLATES_DIR], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "core.context_processors.search_form_context", + "core.context_processors.return_button_context", + ], + }, + }, +] + +WSGI_APPLICATION = "core.wsgi.application" + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + +LANGUAGE_CODE = "ru" + +TIME_ZONE = "Europe/Moscow" + +USE_I18N = True + +USE_TZ = True + +STATIC_ROOT = BASE_DIR / "static" + +STATIC_URL = "/static/" + +STATICFILES_DIRS = (BASE_DIR / "staticfiles",) + +STATICFILES_FINDERS = ( + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", +) + +LOGIN_REDIRECT_URL = "main:main" + +MEDIA_URL = "/media/" + +LOGIN_URL = "login" + +MEDIA_ROOT = BASE_DIR / "media" + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +AUTH_USER_MODEL = "users.User" + +PHONENUMBER_DEFAULT_REGION = "RU" + +PHONENUMBER_DEFAULT_FORMAT = "INTERNATIONAL" + +# LOGIN_REDIRECT_URL = '/players/' + +LOGOUT_REDIRECT_URL = "login" + +SWAGGER_SETTINGS = { + "SECURITY_DEFINITIONS": { + "api_key": {"type": "apiKey", "name": "X-API-KEY", "in": "header"} + }, + "USE_SESSION_AUTH": False, + "DEFAULT_API_URL": "http://127.0.0.1:8000/api/", +} + +REST_FRAMEWORK = { + "DEFAULT_PERMISSION_CLASSES": [ + "rest_framework.permissions.AllowAny", + ], + "DEFAULT_AUTHENTICATION_CLASSES": [], + "DEFAULT_RENDERER_CLASSES": ( + "rest_framework.renderers.JSONRenderer", + "rest_framework.renderers.BrowsableAPIRenderer", + ), + "DEFAULT_PARSER_CLASSES": ( + "rest_framework.parsers.JSONParser", + "rest_framework.parsers.FormParser", + "rest_framework.parsers.MultiPartParser", + ), +} + +PROCESSING_SERVICE_BASE_URL = env('PROCESSING_SERVICE_BASE_URL', default='http://127.0.0.1:8010/') + +API_DOCS_KEY = env("API_DOCS_KEY", default="8f2d9e1b2c4e6f") + +YANDEX_DISK_OAUTH_TOKEN = env("YANDEX_DISK_OAUTH_TOKEN", default="ya_oauth_token") + +MESSAGE_TAGS = { + messages.DEBUG: "alert-secondary", + messages.INFO: "alert-info", + messages.SUCCESS: "alert-success", + messages.WARNING: "alert-warning", + messages.ERROR: "alert-danger", +} diff --git a/adaptive_hockey_federation/core/config/dev_settings.py b/adaptive_hockey_federation/core/config/dev_settings.py new file mode 100644 index 00000000..868093c5 --- /dev/null +++ b/adaptive_hockey_federation/core/config/dev_settings.py @@ -0,0 +1,94 @@ +from .base_settings import * + +ROOT_DIR = BASE_DIR.parent + +env.read_env(ROOT_DIR / ".env") + +DEV_APPS = [ + "django_extensions", + "debug_toolbar", +] + +INSTALLED_APPS += DEV_APPS + +MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") + +INTERNAL_IPS = [ + "127.0.0.1", +] + +DATABASES = { + "default": { + "ENGINE": env("DB_ENGINE", default="django.db.backends.postgresql"), + "NAME": env("POSTGRES_DB", default="postgres_db"), + "USER": env("POSTGRES_USER", default="postgres_user"), + "PASSWORD": env("POSTGRES_PASSWORD", default="postgres_password"), + "HOST": env("DB_HOST", default="localhost"), + "PORT": env("DB_PORT", default="5432"), + } +} + +DJANGO_SUPERUSER_USERNAME = env("DJANGO_SUPERUSER_USERNAME", default="admin") +DJANGO_SUPERUSER_EMAIL = env( + "DJANGO_SUPERUSER_EMAIL", default="admin@admin.ru" +) +DJANGO_SUPERUSER_PASSWORD = env("DJANGO_SUPERUSER_PASSWORD", default="admin") + +FIXSTURES_DIR = BASE_DIR / "core" / "fixtures" +JSON_PARSER_FILE = "data.json" +FIXSTURES_FILE = FIXSTURES_DIR / JSON_PARSER_FILE +DB_DUMP_FILE = FIXSTURES_DIR / "db_dump.json" +RESOURSES_ROOT = BASE_DIR / "resourses" + +# Важен порядок ключей для вставки/удаления +FILE_MODEL_MAP = { + "main_player_team": "Player", + "main_player": "Player", + "main_team": "Team", + "main_staffteammember": "StaffTeamMember", + "main_staffmember": "StaffMember", + "main_city": "City", + "main_diagnosis": "Diagnosis", + "main_nosology": "Nosology", + "main_disciplinelevel": "DisciplineLevel", + "main_disciplinename": "DisciplineName", +} + +EMAIL_BACKEND = env.str( + "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" +) + +EMAIL_TEMPLATE_NAME = "emailing/email.html" + +EMAIL_HOST = env.str("EMAIL_HOST", default="smtp.yandex.ru") + +try: + EMAIL_PORT = env.int("EMAIL_PORT", default=587) +except ValueError: + EMAIL_PORT = 587 + +EMAIL_HOST_USER = env.str("EMAIL_HOST_USER", default="example@yandex.ru") + +EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD", default="password") + +EMAIL_USE_TLS = env.str("EMAIL_USE_TLS", default=True) + +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER + +SERVER_EMAIL = EMAIL_HOST_USER + +EMAIL_ADMIN = EMAIL_HOST_USER + +ADMIN_PAGE_ORDERING = { + "main": [ + "Player", + "Team", + "StaffMember", + "DisciplineName", + "DisciplineLevel", + "City", + "Nosology", + "Diagnosis", + "GameDataPlayer", + ], +} diff --git a/adaptive_hockey_federation/core/config/openpyxl/__init__.py b/adaptive_hockey_federation/core/config/openpyxl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/config/openpyxl/settings.py b/adaptive_hockey_federation/core/config/openpyxl/settings.py new file mode 100644 index 00000000..0f4080f9 --- /dev/null +++ b/adaptive_hockey_federation/core/config/openpyxl/settings.py @@ -0,0 +1,39 @@ +from openpyxl.styles import Alignment, Border, Font, PatternFill, Side + +FONT: Font = Font( + name="Calibri", + bold=True, + italic=False, + vertAlign=None, + underline="none", + strike=False, +) + +BORDER_LINE: Side = Side(border_style="thin", color="000000") + +TITLE_HEIGHT: int = 25 +TITLE_FONT: Font = FONT +TITLE_FONT.size = 13 +TITLE_FILL: PatternFill = PatternFill(patternType="solid", fgColor="b4c7dc") + +HEADERS_HEIGHT: int = 20 +HEADERS_FONT: Font = FONT +HEADERS_FONT.size = 12 +HEADERS_FILL: PatternFill = PatternFill(patternType="solid", fgColor="2a6099") +HEADERS_BORDER: Border = Border( + top=BORDER_LINE, + bottom=BORDER_LINE, + left=BORDER_LINE, + right=BORDER_LINE, +) + +ALIGNMENT_CENTER: Alignment = Alignment( + horizontal="general", + vertical="center", + text_rotation=0, + wrap_text=False, + shrink_to_fit=False, + indent=0, +) + +ROWS_FILL: PatternFill = PatternFill(patternType="solid", fgColor="dee6ef") diff --git a/adaptive_hockey_federation/core/config/prod_settings.py b/adaptive_hockey_federation/core/config/prod_settings.py new file mode 100644 index 00000000..3431031f --- /dev/null +++ b/adaptive_hockey_federation/core/config/prod_settings.py @@ -0,0 +1,82 @@ +from .base_settings import * + +ROOT_DIR = BASE_DIR.parent + +env.read_env(ROOT_DIR / ".env") + +DEBUG = False + +INTERNAL_IPS = [ + "127.0.0.1", +] + +DATABASES = { + "default": { + "ENGINE": env("DB_ENGINE"), + "NAME": env("POSTGRES_DB"), + "USER": env("POSTGRES_USER"), + "PASSWORD": env("POSTGRES_PASSWORD"), + "HOST": env("DB_HOST"), + "PORT": env("DB_PORT"), + } +} +# TODO: возможно удаления т.к относится к parser +FIXSTURES_DIR = BASE_DIR / "core" / "fixtures" +# TODO: возможно удаления т.к относится к parser +JSON_PARSER_FILE = "data.json" +# TODO: возможно удаления т.к относится к parser +FIXSTURES_FILE = FIXSTURES_DIR / JSON_PARSER_FILE +RESOURSES_ROOT = BASE_DIR / "resourses" + +# Важен порядок ключей для вставки/удаления +FILE_MODEL_MAP = { + "main_player_team": "Player", + "main_player": "Player", + "main_team": "Team", + "main_staffteammember": "StaffTeamMember", + "main_staffmember": "StaffMember", + "main_city": "City", + "main_diagnosis": "Diagnosis", + "main_nosology": "Nosology", + "main_disciplinelevel": "DisciplineLevel", + "main_disciplinename": "DisciplineName", +} + +EMAIL_BACKEND = env.str( + "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" +) + +EMAIL_TEMPLATE_NAME = "emailing/email.html" + +EMAIL_HOST = env.str("EMAIL_HOST", default="smtp.yandex.ru") + +try: + EMAIL_PORT = env.int("EMAIL_PORT", default=587) +except ValueError: + EMAIL_PORT = 587 + +EMAIL_HOST_USER = env.str("EMAIL_HOST_USER", default="example@yandex.ru") + +EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD", default="password") + +EMAIL_USE_TLS = env.str("EMAIL_USE_TLS", default=True) + +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER + +SERVER_EMAIL = EMAIL_HOST_USER + +EMAIL_ADMIN = EMAIL_HOST_USER + +ADMIN_PAGE_ORDERING = { + "main": [ + "Player", + "Team", + "StaffMember", + "DisciplineName", + "DisciplineLevel", + "City", + "Nosology", + "Diagnosis", + "GameDataPlayer", + ], +} diff --git a/adaptive_hockey_federation/core/config/test_settings.py b/adaptive_hockey_federation/core/config/test_settings.py new file mode 100644 index 00000000..c8ac0fa3 --- /dev/null +++ b/adaptive_hockey_federation/core/config/test_settings.py @@ -0,0 +1,8 @@ +from .base_settings import * + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} diff --git a/adaptive_hockey_federation/core/constants.py b/adaptive_hockey_federation/core/constants.py new file mode 100644 index 00000000..3e43e633 --- /dev/null +++ b/adaptive_hockey_federation/core/constants.py @@ -0,0 +1,195 @@ +from dataclasses import dataclass +from enum import IntEnum, StrEnum + + +class UserConstans(IntEnum): + """Константы для приложения users.""" + + NAME_MAX_LENGTH = 256 + EMAIL_MAX_LENGTH = 256 + QUERY_SET_LENGTH = 15 + + +class Role(StrEnum): + """Роли пользователей.""" + + AGENT = "Представитель команды" + MODERATOR = "Модератор" + ADMIN = "Администратор" + SUPERUSER = "Администратор" + + +ROLES_CHOICES = ( + (Role.AGENT, "Представитель команды"), + (Role.MODERATOR, "Модератор"), + (Role.ADMIN, "Администратор"), +) + + +class Group(StrEnum): + """Группы ролей пользоватлей.""" + + ADMINS = "Администраторы" + MODERATORS = "Модераторы" + AGENTS = "Представители команд" + + +GROUPS_BY_ROLE = { + Role.ADMIN: Group.ADMINS, + Role.AGENT: Group.AGENTS, + Role.MODERATOR: Group.MODERATORS, + Role.SUPERUSER: Group.ADMINS, +} + + +class Discipline(StrEnum): + """Виды дисциплин в хоккее.""" + + SLEDGE_HOCKEY = "Следж-хоккей" + BLIND_HOCKEY = "Хоккей для незрячих" + SPECIAL_HOCKEY = "Специальный хоккей" + ROLLER_HOCKEY = "Роликовый следж-хоккей" + + +DISCIPLINE_LEVELS = { + Discipline.SLEDGE_HOCKEY: (1, 2, 3, 4, 5, 6), + Discipline.ROLLER_HOCKEY: (1, 2, 3, 4, 5, 6), + Discipline.BLIND_HOCKEY: ("B1", "B2", "B3", "B4", "B5", "б/к"), + Discipline.SPECIAL_HOCKEY: ("A", "B", "C"), +} + + +class MainConstantsInt(IntEnum): + """Константы int для main/models.py.""" + + CHAR_FIELD_LENGTH = 256 + CLASS_FIELD_LENGTH = 10 + DEFAULT_VALUE = 0 + + +class MainConstantsStr(StrEnum): + """Константы str для main/models.py.""" + + EMPTY_VALUE_DISPLAY = "" + + +class Gender(StrEnum): + """Пол.""" + + MAN = "Мужской" + WOMAN = "Женский" + + +GENDER_CHOICES = ( + (Gender.MAN.value, "Мужской"), + (Gender.WOMAN.value, "Женский"), +) + + +class PlayerPosition(StrEnum): + """Позиции игроков.""" + + STRIKER = "Нападающий" + BOBBER = "Поплавок" + GOALKEEPER = "Вратарь" + DEFENDER = "Защитник" + + +PLAYER_POSITION_CHOICES = ( + (PlayerPosition.STRIKER.value, "Нападающий"), + (PlayerPosition.BOBBER.value, "Поплавок"), + (PlayerPosition.GOALKEEPER.value, "Вратарь"), + (PlayerPosition.DEFENDER.value, "Защитник"), +) + + +class StaffPosition(StrEnum): + """Роли представителей команд.""" + + TRAINER = "тренер" + OTHER = "пушер-тьютор" + + +STAFF_POSITION_CHOICES = ( + (StaffPosition.TRAINER.value, "тренер"), + (StaffPosition.OTHER.value, "пушер-тьютор"), +) + + +class TimeFormat: + """Форматы времени.""" + + TIME_FORMAT = "%H-%M-%S" + + +class AgeLimits(IntEnum): + """Возростные лимиты.""" + + MIN_AGE_PLAYER = 6 + MAX_AGE_PLAYER = 18 + + +FORM_HELP_TEXTS = { + "identity_document": ( + "Введите данные в формате 'Паспорт ХХХХ ХХХХХХ' или " + "'Свидетельство о рождении X-XX XXXXXX'" + ), + "birthday": ( + f"Возраст должен быть от {AgeLimits.MIN_AGE_PLAYER}" + f"до {AgeLimits.MAX_AGE_PLAYER} лет" + ), + "available_teams": ("Список доступных команд перемещение двойным щелчком"), + "email": ( + "Введите актуальную электронную почту в формате example@domen.ru" + ), + "role": ("Выберите роль которая соответствует пользователю"), + "player_teams": ( + "Список команд в которых состоит игрок удаление двойным щелчком" + ), + "staff_teams": ( + "Список команд в которых состоит сотрудник удаление двойным щелчком" + ), + "available_disciplines": ( + "Список доступных дисциплин перемещение двойным щелчком" + ), + "disciplines": "Список дисциплин в соревновании", +} + + +@dataclass +class FileConstants: + """Константы для файлов.""" + + FILE_RESOLUTION = ("png", "jpeg", "jpg", "pdf") + MAX_UPLOAD_SIZE: int = 10485760 + MAX_UPLOAD_SIZE_MB: str = str(int(MAX_UPLOAD_SIZE / (1024 * 1024))) + " MB" + + +SEARCH_ALIAS = { + "surname": "surname", + "name": "name", + "birthday": "birthday", + "gender": "gender", + "number": "surname", + "discipline": "discipline__discipline_name_id__name", + "diagnosis": "diagnosis__name", + "city": "city_name", +} + + +class Directory: + """Директории.""" + + GAMES = "games" + PLAYER_VIDEO_DIR = "player_video" + UNLOAD_DIR = "unloads_data" + + +class YadiskDirectory(StrEnum): + """Директории на Яндекс.Диске.""" + + GAMES = "games" + PLAYER_GAMES = "player_games" + + +PLAYER_GAME_NAME = "{surname}_{name[0]}_{patronymic[0]}_{game_name}.mp4" diff --git a/adaptive_hockey_federation/core/context_processors.py b/adaptive_hockey_federation/core/context_processors.py new file mode 100644 index 00000000..3d5d10b2 --- /dev/null +++ b/adaptive_hockey_federation/core/context_processors.py @@ -0,0 +1,21 @@ +from main.schemas.main_schema import no_search_pages, show_return_button + + +def search_form_context(request): + """ + Отвечает за правильное отображение поиска на страницах. + + Если нужно где-то убрать поле поиска, нужно внести изменения в список + main/schemas/main_schema.py. + """ + return {"no_search_pages": no_search_pages} + + +def return_button_context(request): + """ + Отвечает за отображение кнопки назад. + + Если следует добавить кнопку назад, нужно внести изменения в список + main/schemas/main_schema.py. + """ + return {"show_return_button": show_return_button} diff --git a/adaptive_hockey_federation/core/fixtures/fill_random_diagnosis.py b/adaptive_hockey_federation/core/fixtures/fill_random_diagnosis.py new file mode 100644 index 00000000..ebf64021 --- /dev/null +++ b/adaptive_hockey_federation/core/fixtures/fill_random_diagnosis.py @@ -0,0 +1,24 @@ +import json +import random + + +# TODO: (Скрипт который заполняет диагнозы игроков, +# но его можно убрать, как будут реальные данные +# по диагнозам игроков.) + + +with open("main_player.json", "r", encoding="utf-8") as f: + players = json.load(f) + +with open("main_diagnosis.json", "r", encoding="utf-8") as f: + diagnoses = json.load(f) + +diagnosis_ids = [diagnosis["id"] for diagnosis in diagnoses] + +for player in players: + player["diagnosis_id"] = random.choice(diagnosis_ids) + +with open("main_player.json", "w", encoding="utf-8") as f: + json.dump(players, f, ensure_ascii=False, indent=4) + +print("Диагнозы успешно назначены и сохранены в main_player_updated.json") diff --git a/adaptive_hockey_federation/core/logging.py b/adaptive_hockey_federation/core/logging.py new file mode 100644 index 00000000..7246ef61 --- /dev/null +++ b/adaptive_hockey_federation/core/logging.py @@ -0,0 +1,9 @@ +import logging + + +def configure_logging(level: int = logging.INFO) -> None: + logging.basicConfig(level=level, + datefmt="%Y-%m-%d %H:%M:%S", + format="[%(asctime)s.%(msecs)03d] %(funcName)20s" + "%(module)s:%(lineno)d %(levelname)-8s - %(message)s", + ) diff --git a/adaptive_hockey_federation/core/management/commands/__init__.py b/adaptive_hockey_federation/core/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/management/commands/export-db.py b/adaptive_hockey_federation/core/management/commands/export-db.py new file mode 100644 index 00000000..f4b88dba --- /dev/null +++ b/adaptive_hockey_federation/core/management/commands/export-db.py @@ -0,0 +1,32 @@ +from django.core.management import call_command +from django.core.management.base import BaseCommand + +from adaptive_hockey_federation.core.config.dev_settings import DB_DUMP_FILE + + +class Command(BaseCommand): + """Класс экспорта данных из БД.""" + + help = "Экспорт данных из базы данных в JSON-файл" + + def handle(self, *args, **options): + """Экспортирует данные из БД.""" + try: + call_command( + "dumpdata", + exclude=["contenttypes", "auth.permission"], + natural_foreign=True, + natural_primary=True, + indent=2, + output=DB_DUMP_FILE, + ) + + return self.stdout.write( + self.style.SUCCESS( + f"База данных экспортирована в {DB_DUMP_FILE}", + ), + ) + except Exception as e: + return self.stdout.write( + self.style.ERROR(f"Ошибка при экспорте данных: {str(e)}"), + ) diff --git a/adaptive_hockey_federation/core/management/commands/fill-db.py b/adaptive_hockey_federation/core/management/commands/fill-db.py new file mode 100644 index 00000000..b5ba070b --- /dev/null +++ b/adaptive_hockey_federation/core/management/commands/fill-db.py @@ -0,0 +1,140 @@ +import json + +from django.apps import apps +from django.core.management.base import BaseCommand +from django.db import connection, transaction +from main.models import Diagnosis + +from adaptive_hockey_federation.core.config.dev_settings import ( + FILE_MODEL_MAP, + FIXSTURES_DIR, +) + + +class Command(BaseCommand): + """Класс для парсинга данных и их записи в БД.""" + + help = "Запуск парсера офисных документов, и запись их в БД." + + def add_arguments(self, parser): + """Аргументы.""" + parser.add_argument( + "-f", + "--fixtures", + action="store_true", + help="Фикстуры с реальными данными для таблиц.", + ) + + @transaction.atomic + def load_real_data(self) -> None: # noqa: C901 + """Загрузка реальных данных из JSON.""" + with connection.cursor() as cursor: + for table_name in FILE_MODEL_MAP.keys(): + try: + cursor.execute(f"TRUNCATE TABLE {table_name} CASCADE") + except Exception as e: + return self.stdout.write( + self.style.WARNING( + f"Не удалось очистить таблицу {table_name}: {str(e)}", # noqa: E501 + ), + ) + + items = list(FILE_MODEL_MAP.items()) + items.reverse() + for table_name, model_class in items: + file_path = FIXSTURES_DIR / f"{table_name}.json" + app_label, model_name = table_name.split("_", 1) + model = apps.get_model(app_label, model_name) + try: + with open(file_path, "r", encoding="utf-8") as file: + data = json.load(file) + + for item in data: + if table_name == "main_team": + team_data = { + "id": item["id"], + "name": item["name"], + "city_id": item["city_id"], + "discipline_name_id": item["discipline_name_id"], + "curator_id": 1, + } + instance = model(**team_data) + elif table_name == "main_player": + disciplines = self.get_disciplines() + diagnosis = None + if item.get("diagnosis_id"): + try: + diagnosis = Diagnosis.objects.get( + pk=item["diagnosis_id"], + ) + except Diagnosis.DoesNotExist: + print( + f"Диагноз с id {item.get('diagnosis_id')} отсутсвует.", # noqa: E501 + ) + player_data = { + "id": item["id"], + "surname": item["surname"], + "name": item["name"], + "patronymic": item["patronymic"], + "birthday": item["birthday"], + "gender": item["gender"], + "level_revision": item["level_revision"], + "position": item["position"], + "number": item["number"], + "is_captain": item["is_captain"], + "is_assistent": item["is_assistent"], + "identity_document": item["identity_document"], + "diagnosis": diagnosis, + "diagnosis_id": item["diagnosis_id"], + "discipline_name_id": disciplines[ + item["discipline_id"] + ]["discipline_name_id"], + "discipline_level_id": disciplines[ + item["discipline_id"] + ]["discipline_level_id"], + } + instance = model(**player_data) + else: + instance = model(**item) + instance.save() + + self.stdout.write( + self.style.SUCCESS( + f"Данные из {table_name}.json успешно загружены в модель {model_class}", # noqa: E501 + ), + ) + + except FileNotFoundError: + self.stdout.write( + self.style.WARNING(f"Файл {table_name}.json не найден"), + ) + except Exception as e: + self.stdout.write( + self.style.ERROR( + f"Ошибка при загрузке данных из {table_name}.json: {str(e)}", # noqa: E501 + ), + ) + + def handle(self, *args, **options): + """Запись данных в БД.""" + fixtures = options.get("fixtures") + if fixtures: + self.load_real_data() + + def get_disciplines(self) -> dict: + """Получение диспциплин.""" + with open( + FIXSTURES_DIR / "main_discipline.json", + "r", + encoding="utf-8", + ) as file: + data = json.load(file) + disciplines = { + None: {"discipline_level_id": None, "discipline_name_id": None}, + } + for item in data: + disciplines[item["id"]] = { + "discipline_level_id": item["discipline_level_id"], + "discipline_name_id": item["discipline_name_id"], + } + return disciplines diff --git a/adaptive_hockey_federation/core/management/commands/fill-test-db.py b/adaptive_hockey_federation/core/management/commands/fill-test-db.py new file mode 100644 index 00000000..8d868b41 --- /dev/null +++ b/adaptive_hockey_federation/core/management/commands/fill-test-db.py @@ -0,0 +1,207 @@ +from random import randint + +from core.constants import STAFF_POSITION_CHOICES, Role +from django.core.management.base import BaseCommand +from main.data_factories.factories import ( + CompetitionFactory, + DiagnosisFactory, + DocumentFactory, + GameFactory, + PlayerFactory, + StaffTeamMemberFactory, + TeamFactory, + GameDataPlayerFactory, +) +from main.data_factories.utils import updates_for_players +from main.models import Player +from unloads.factories import UnloadFactory +from users.factories import UserFactory + +AMOUNT_ADMIN = 3 +AMOUNT_MODERATOR = 2 +AMOUNT_AGENT = 15 +AMOUNT_COACH = 15 +AMOUNT_UNLOADS = 4 +AMOUNT_OTHERS = 10 +USERS = { + Role.ADMIN: AMOUNT_ADMIN, + Role.MODERATOR: AMOUNT_MODERATOR, + Role.AGENT: AMOUNT_AGENT, +} +STAFF = { + STAFF_POSITION_CHOICES[0][1]: AMOUNT_COACH, + STAFF_POSITION_CHOICES[1][1]: AMOUNT_OTHERS, +} + + +class Command(BaseCommand): + """Класс для наполнения базы данных тестовыми данными.""" + + help = "Наполнение базы данных тестовыми данными." + + def add_arguments(self, parser): + """Добавляет новые аргументы для командной строки.""" + parser.add_argument( + "-u", + "--users", + action="store_true", + help="Фикстуры для таблицы Users", + ) + parser.add_argument( + "-d", + "--diagnosis", + action="store_true", + help="Фикстуры для таблицы Diagnosis", + ) + parser.add_argument( + "-s", + "--staffteam", + action="store_true", + help="Фикстуры для таблицы StaffTeamMember", + ) + parser.add_argument( + "-t", + "--team", + action="store_true", + help="Фикстуры для таблицы Team", + ) + parser.add_argument( + "-p", + "--player", + action="store_true", + help="Фикстуры для таблицы Player", + ) + parser.add_argument( + "-doc", + "--document", + action="store_true", + help="Фикстуры для таблицы Document", + ) + parser.add_argument( + "-e", + "--competition", + action="store_true", + help="Фикстуры для таблицы Competition", + ) + parser.add_argument( + "-g", + "--game", + action="store_true", + help="Фикстуры для таблицы Game", + ) + parser.add_argument( + "-un", + "--unload", + action="store_true", + help="Фикстуры для таблицы Unloads", + ) + parser.add_argument( + "-json", + "--json-player-data", + action="store_true", + help="Фикстуры для таблицы JSON Player Data", + ) + parser.add_argument( + "-a", + "--amount", + type=int, + default=10, + help="Количество фикстур для создания", + ) + + def handle(self, *args, **options): # noqa: C901 + """Метод для наполнения базы тестовыми данными.""" + test_users = options.get("users", False) + diagnosis = options.get("diagnosis", False) + staff_team = options.get("staffteam", False) + team = options.get("team", False) + player = options.get("player", False) + document = options.get("document", False) + competition = options.get("competition", False) + unload = options.get("unload", False) + json_player_data = options.get("json_player_data", False) + game = options.get("game", False) + amount = options.get("amount") + + if test_users: + users_amount = sum(USERS.values()) + for role, amount in USERS.items(): + UserFactory.create_batch(amount, role=role) + return self.stdout.write( + self.style.SUCCESS( + f"{users_amount} фикстур для таблицы User создано!", + ), + ) + if diagnosis: + DiagnosisFactory.create_batch(amount) + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы Diagnosis создано!", + ), + ) + if staff_team: + staff_amount = sum(STAFF.values()) + for staff_position, amount in STAFF.items(): + StaffTeamMemberFactory.create_batch( + amount, + staff_position=staff_position, + ) + return self.stdout.write( + self.style.SUCCESS( + f"{staff_amount} фикстур для таблицы " + "StaffTeamMember создано!", + ), + ) + if team: + TeamFactory.create_batch(amount) + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы Team созданы!", + ), + ) + if player: + PlayerFactory.create_batch(amount) + updates_for_players() + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы Player созданы!", + ), + ) + if document: + players = Player.objects.all() + for player in players: + num_docs = randint(1, 5) + DocumentFactory.create_batch(num_docs, player=player) + return self.stdout.write( + self.style.SUCCESS( + f"{num_docs} фикстур для таблицы Document созданы!", + ), + ) + if competition: + CompetitionFactory.create_batch(amount) + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы Competition созданы!", + ), + ) + if game: + GameFactory.create_batch(amount) + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы Game созданы.", + ), + ) + if unload: + UnloadFactory.create_batch(AMOUNT_UNLOADS) + return self.stdout.write( + self.style.SUCCESS( + f"{AMOUNT_UNLOADS} фикстуры для Unloads созданы.", + ), + ) + if json_player_data: + GameDataPlayerFactory.create_batch(amount) + return self.stdout.write( + self.style.SUCCESS( + f"{amount} фикстур для таблицы JSON Player Data созданы.", + ), + ) diff --git a/adaptive_hockey_federation/core/management/commands/fill-test-stage.py b/adaptive_hockey_federation/core/management/commands/fill-test-stage.py new file mode 100644 index 00000000..4e4da907 --- /dev/null +++ b/adaptive_hockey_federation/core/management/commands/fill-test-stage.py @@ -0,0 +1,33 @@ +import subprocess + +from django.core.management.base import BaseCommand + +base_commands = "python manage.py fill-test-db " +commands = ( + "--users", + "--diagnosis --amount 8", + "--discipline --amount 3", + "--team --amount 20", + "--staffteam", + "--player --amount 300", + "--document", + "--competition --amount 10", +) + + +class Command(BaseCommand): + """Класс для запуска наполнения базы данных тестовыми данными.""" + + help = "Наполнение базы данных тестовыми данными." + + def handle(self, *args, **options): + """Выполняет запуск наполнения базы данных со всеми аргументами.""" + for argument in commands: + result = subprocess.run( + base_commands + argument, + shell=True, + capture_output=True, + text=True, + ) + print(self.style.SUCCESS(result.stdout)) + return self.style.SUCCESS("Создание фикстур завершено!") diff --git a/adaptive_hockey_federation/core/management/commands/import-db.py b/adaptive_hockey_federation/core/management/commands/import-db.py new file mode 100644 index 00000000..bff63edf --- /dev/null +++ b/adaptive_hockey_federation/core/management/commands/import-db.py @@ -0,0 +1,54 @@ +import json +import os + +from django.apps import apps +from django.core.management import call_command +from django.core.management.base import BaseCommand +from django.db import connection + +from adaptive_hockey_federation.core.config.dev_settings import DB_DUMP_FILE + + +class Command(BaseCommand): + """Класс импорта данных в БД.""" + + help = "Импорт данных из JSON-файла в базу данных" + + def handle(self, *args, **options): + """Импортирует данные в БД.""" + if not os.path.exists(DB_DUMP_FILE): + return self.stdout.write( + self.style.ERROR(f"Файл {DB_DUMP_FILE} не найден"), + ) + + with open(DB_DUMP_FILE, "r") as f: + data = json.load(f) + models_to_clear = set(item["model"] for item in data) + + # Очистка таблиц, в которые импортируются данные, + # для исключения ошибки unique constraint + with connection.cursor() as cursor: + for model_name in models_to_clear: + try: + app_label, model = model_name.split(".") + model_class = apps.get_model(app_label, model) + table_name = model_class._meta.db_table + cursor.execute(f"TRUNCATE TABLE {table_name} CASCADE") + except Exception as e: + return self.stdout.write( + self.style.WARNING( + f"Не удалось очистить таблицу для модели {model_name}: {str(e)}", # noqa: E501 + ), + ) + + try: + call_command("loaddata", DB_DUMP_FILE) + return self.stdout.write( + self.style.SUCCESS( + f"Данные из {DB_DUMP_FILE} импортированы в базу данных", + ), + ) + except Exception as e: + return self.stdout.write( + self.style.ERROR(f"Ошибка при импорте данных: {str(e)}"), + ) diff --git a/adaptive_hockey_federation/core/migrations/__init__.py b/adaptive_hockey_federation/core/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/permissions.py b/adaptive_hockey_federation/core/permissions.py new file mode 100644 index 00000000..98138aad --- /dev/null +++ b/adaptive_hockey_federation/core/permissions.py @@ -0,0 +1,12 @@ +from django.contrib.auth.mixins import AccessMixin + + +class AdminRequiredMixin(AccessMixin): + """Миксин наделяющий правом доступа только администратора.""" + + def dispatch(self, request, *args, **kwargs): + """Обрабатывает запрос, если пользователь админ, или переадресует.""" + if request.user.is_authenticated: + if request.user.is_moderator or request.user.is_agent: + return self.handle_no_permission() + return super().dispatch(request, *args, **kwargs) diff --git a/adaptive_hockey_federation/core/templatetags/__init__.py b/adaptive_hockey_federation/core/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/core/templatetags/user_filters.py b/adaptive_hockey_federation/core/templatetags/user_filters.py new file mode 100644 index 00000000..9cbf1937 --- /dev/null +++ b/adaptive_hockey_federation/core/templatetags/user_filters.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + + +@register.filter +def addclass(field, css): + return field.as_widget(attrs={"class": css}) diff --git a/adaptive_hockey_federation/core/urls.py b/adaptive_hockey_federation/core/urls.py new file mode 100644 index 00000000..e8be4581 --- /dev/null +++ b/adaptive_hockey_federation/core/urls.py @@ -0,0 +1,38 @@ +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from rest_framework import permissions + +schema_view = get_schema_view( + openapi.Info( + title="API", + default_version="v1", + description="API для управления данными игроков и игр", + ), + public=True, + permission_classes=(permissions.AllowAny,), +) + +urlpatterns = [ + path("admin/", admin.site.urls), + path( + "api/docs/", + schema_view.with_ui("swagger", cache_timeout=0), + name="schema-swagger-ui", + ), + path("", include("main.urls", namespace="main")), + path("", include("users.urls", namespace="users")), + path("", include("competitions.urls", namespace="competitions")), + path("", include("analytics.urls", namespace="analytics")), + path("", include("unloads.urls", namespace="unloads")), + path("", include("games.urls", namespace="games")), + path("__debug__/", include("debug_toolbar.urls")), + path("auth/", include("django.contrib.auth.urls")), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +handler404 = "core.views.not_found" +handler403 = "core.views.forbidden" +handler500 = "core.views.internal_server_error" diff --git a/adaptive_hockey_federation/core/utils.py b/adaptive_hockey_federation/core/utils.py new file mode 100644 index 00000000..d8cfaad3 --- /dev/null +++ b/adaptive_hockey_federation/core/utils.py @@ -0,0 +1,182 @@ +import os +from datetime import datetime +from typing import Any, List, Optional + + +from core.constants import AgeLimits, FileConstants, TimeFormat +from core.config.openpyxl.settings import ( + ALIGNMENT_CENTER, + HEADERS_BORDER, + HEADERS_FILL, + HEADERS_FONT, + HEADERS_HEIGHT, + ROWS_FILL, + TITLE_FILL, + TITLE_FONT, + TITLE_HEIGHT, +) +from django.conf import settings +from main.models import Player +from django.core.files.uploadedfile import InMemoryUploadedFile +from django.db.models import QuerySet +from openpyxl import Workbook +from openpyxl.worksheet.worksheet import Worksheet + + +def generate_file_name(filename: str, prefix: str) -> str: + filename, file_extension = os.path.splitext(filename) + return ( + f"{prefix}-{datetime.now().strftime(TimeFormat.TIME_FORMAT)}" + f"{file_extension}" + ) + + +def is_uploaded_file_valid(file: InMemoryUploadedFile) -> bool: + if ( + file.content_type + and file.size + and file.content_type.split("/")[1] in FileConstants.FILE_RESOLUTION + and file.size <= FileConstants.MAX_UPLOAD_SIZE + ): + return True + return False + + +def min_date(): + now = datetime.now() + month_day = format(now.strftime("%m-%d")) + return f"{str(now.year - AgeLimits.MAX_AGE_PLAYER)}-{month_day}" + + +def max_date(): + now = datetime.now() + month_day = format(now.strftime("%m-%d")) + return f"{str(now.year - AgeLimits.MIN_AGE_PLAYER)}-{month_day}" + + +def column_width(workbook: Worksheet) -> None: + for col in workbook.columns: + max_length = 0 + column = col[0].column_letter + for cell in col: + if len(str(cell.value)) > max_length: + max_length = len(str(cell.value)) + adjusted_width = max_length + 2 + workbook.column_dimensions[column].width = adjusted_width + + +def export_excel( + queryset: QuerySet, + filename: str, + title: str, + excluded_fields: Optional[List[str]] = None, + fields_order: Optional[List[str]] = None, +) -> str: + """ + Выгрузка данных в excel (формат xlsx). + + После создания файла возвращает его имя. + """ + if excluded_fields is None: + excluded_fields = [] + + wb = Workbook() + del wb["Sheet"] + ws: Worksheet = wb.create_sheet("Лист1") + ws.append(["", title]) + + if queryset: + headers, fields = get_fields_and_headers( + queryset, + excluded_fields, + fields_order, + ) + ws.append(headers) + add_data_to_worksheet(ws, queryset, fields) + + apply_styles(ws) + + file_path = save_workbook(wb, filename) + return file_path + + +def get_fields_and_headers(queryset, excluded_fields, fields_order): + model_fields = queryset.model._meta.fields + fields_dict = { + field.name: str(field.verbose_name) + for field in model_fields + if field.name not in excluded_fields + } + + if fields_order: + headers = [ + fields_dict[field] + for field in fields_order + if field in fields_dict + ] + fields = [field for field in fields_order if field in fields_dict] + else: + headers = list(fields_dict.values()) + fields = list(fields_dict.keys()) + + return headers, fields + + +def add_data_to_worksheet(ws, queryset, fields): + for obj in queryset: + row: List[Any] = [] + for field in fields: + value = getattr(obj, field) + if isinstance(value, datetime): + value = value.strftime("%Y-%m-%d %H:%M:%S") + else: + if hasattr(value, "__str__"): + value = value.__str__() + + if isinstance(obj, Player): + field_map = { + "is_captain": "да" if obj.is_captain else "", + "is_assistent": "да" if obj.is_assistent else "", + } + if field in field_map: + value = field_map[field] + + row.append(value) + + ws.append(row) + + +def apply_styles(ws): + column_width(ws) + + ws.row_dimensions[1].height = TITLE_HEIGHT + ws.row_dimensions[2].height = HEADERS_HEIGHT + + for cell in ws[1]: + cell.fill = TITLE_FILL + cell.font = TITLE_FONT + cell.alignment = ALIGNMENT_CENTER + + for cell in ws[2]: + cell.fill = HEADERS_FILL + cell.font = HEADERS_FONT + cell.alignment = ALIGNMENT_CENTER + cell.border = HEADERS_BORDER + + number_rows = int(ws.dimensions.split(":")[1][1:]) + for i in range(3, number_rows + 1, 2): + for cell in ws[i]: + cell.fill = ROWS_FILL + + +def save_workbook(wb, filename): + media_data_path = os.path.join(settings.MEDIA_ROOT, "unloads_data") + os.makedirs(media_data_path, exist_ok=True) + + timestamp = datetime.now().strftime("%Y.%m.%d_%H%M%S") + base_filename, file_extension = os.path.splitext(filename) + filename_with_timestamp = f"{base_filename}_{timestamp}{file_extension}" + file_path = os.path.join(media_data_path, filename_with_timestamp) + wb.save(file_path) + + return filename_with_timestamp diff --git a/adaptive_hockey_federation/core/validators.py b/adaptive_hockey_federation/core/validators.py new file mode 100644 index 00000000..cfecf8b0 --- /dev/null +++ b/adaptive_hockey_federation/core/validators.py @@ -0,0 +1,49 @@ +import datetime + +from core.constants import AgeLimits +from django.core.exceptions import ValidationError +from django.core.validators import RegexValidator +from django.utils.timezone import now as django_now + + +def fio_validator() -> RegexValidator: + """ + Функция проверки поля. + + 1. Присутствие только кирилических символов + 2. Возможно использование дефиса. + """ + return RegexValidator( + r"^[А-Яа-яё -]+$", + ( + "Строка должны состоять из кирилических символов. " + "Возможно использование дефиса." + ), + ) + + +def validate_date_birth(value: datetime.date): + now = datetime.date.today() + min_date = datetime.date( + now.year - AgeLimits.MAX_AGE_PLAYER, + now.month, + now.day, + ) + max_date = datetime.date( + now.year - AgeLimits.MIN_AGE_PLAYER, + now.month, + now.day, + ) + + if not (min_date <= value <= max_date): + raise ValidationError( + f"Возраст должен быть от {AgeLimits.MIN_AGE_PLAYER}" + f"до {AgeLimits.MAX_AGE_PLAYER} лет", + ) + + +def validate_game_date(date: datetime.date) -> datetime.date: + """Проверка валидности даты игры. Дата не должна быть больше текущей.""" + if date > django_now(): + raise ValidationError("Игра не может проходить в будущем") + return date diff --git a/adaptive_hockey_federation/core/views.py b/adaptive_hockey_federation/core/views.py new file mode 100644 index 00000000..c6d6d055 --- /dev/null +++ b/adaptive_hockey_federation/core/views.py @@ -0,0 +1,33 @@ +from http import HTTPStatus + +from django.shortcuts import render + + +def not_found(request, exception): + """Представление ошибки 404.""" + return render( + request=request, + template_name="error-pages/404.html", + context={"path": request.path}, + status=HTTPStatus.NOT_FOUND, + ) + + +def forbidden(request, exception): + """Представление ошибки 403.""" + return render( + request=request, + template_name="error-pages/403.html", + context={"path": request.path}, + status=HTTPStatus.FORBIDDEN, + ) + + +def internal_server_error(request): + """Представление ошибки 500.""" + return render( + request=request, + template_name="error-pages/500.html", + context={"path": request.path}, + status=HTTPStatus.INTERNAL_SERVER_ERROR, + ) diff --git a/adaptive_hockey_federation/core/wsgi.py b/adaptive_hockey_federation/core/wsgi.py new file mode 100644 index 00000000..bae01bc0 --- /dev/null +++ b/adaptive_hockey_federation/core/wsgi.py @@ -0,0 +1,10 @@ +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + "core.config.prod_settings", +) + +application = get_wsgi_application() diff --git a/adaptive_hockey_federation/games/__init__.py b/adaptive_hockey_federation/games/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/games/admin.py b/adaptive_hockey_federation/games/admin.py new file mode 100644 index 00000000..0827d51d --- /dev/null +++ b/adaptive_hockey_federation/games/admin.py @@ -0,0 +1,135 @@ +from django import forms +from django.contrib import admin +from games.constants import Errors, NumericalValues +from games.models import Game, GamePlayer, GameTeam, GameDataPlayer +from main.models import Team + + +class GameTeamAdminForm(forms.ModelForm): + """ + Кастомная админ-форма для модели GameTeam. + + Включает в себя проверку корректности ввода команд. + """ + + class Meta: + model = Game + fields = ("name",) + + def clean(self): + """Метод, проверяющий корректность ввода команд.""" + form_data = self.data + if form_data.get( + "game_teams-0-name", + ) == form_data.get( + "game_teams-1-name", + ): + raise forms.ValidationError(Errors.CANNOT_PLAY_GAME_AGAINST_SELF) + elif "game_teams-2-name" in form_data: + # На случай появления в форме трёх и более команд + raise forms.ValidationError(Errors.NO_MORE_THAN_TWO_TEAMS_IN_GAME) + return self.cleaned_data + + +class GameTeamsInLine(admin.StackedInline): + """Инлайн для команд, участвующих в игре.""" + + model = GameTeam + form = GameTeamAdminForm + extra = NumericalValues.ADMIN_EXTRA_TEAMS + max_num = NumericalValues.MAX_TEAMS_IN_GAME + min_num = max_num + readonly_fields = ("discipline_name",) + + +class GamePlayersInLine(admin.TabularInline): + """Инлайн для игроков, участвующих в игре.""" + + model = GamePlayer + extra = 0 + readonly_fields = ("name", "number", "game_team") + can_delete = False + + +@admin.register(Game) +class GameAdmin(admin.ModelAdmin): + """Админка для модели игр.""" + + list_display = ("name", "date", "competition", "video_link") + list_filter = ("date", "competition") + search_fields = ("name",) + ordering = ["name"] + inlines = [GameTeamsInLine] + + def save_model(self, request, obj, form, change): + """ + Сохраняет модель игры и связанные с ней модели игроков и команд. + + Дополнительно передаём список команд, чтобы их смог получить и + обработать соответствующий сигнал. + """ + teams = Team.objects.filter( + name__in=[ + form.data.get( + "game_teams-0-name", + ), + form.data.get( + "game_teams-1-name", + ), + ], + ).values_list( + "id", + flat=True, + ) + obj.teams = teams + super().save_model(request, obj, form, change) + + def save_formset(self, request, form, formset, change): + """ + По умолчанию, сохраняет изменения, указанные в inline formset'е. + + В нашем случае в этом нет необходимости, так как созданием связанных + GameTeam и GamePlayers занимаются сигналы, поэтому мы формально + сохраняем изменения, чтобы вызывать сигналы, которые и проведут + необходимые операции. Сами данные из формы будут сброшены. + Если этого не делать, то при сохранении формы возникнут дубликаты + сущностей GameTeam и GamePlayers. + """ + formset.save(commit=False) + + +@admin.register(GameTeam) +class GameTeamAdmin(admin.ModelAdmin): + """Админка для модели команды, участвующей в игре.""" + + list_display = ("name", "discipline_name", "game") + list_filter = ("game",) + search_fields = ("name",) + ordering = ["name"] + inlines = [GamePlayersInLine] + + +@admin.register(GamePlayer) +class GamePlayerAdmin(admin.ModelAdmin): + """Админка для модели игроков, участвующих в игре.""" + + list_display = ("name", "number", "game_team") + list_filter = ("game_team",) + search_fields = ("name",) + ordering = ["name"] + + +@admin.register(GameDataPlayer) +class GameDataPlayerAdmin(admin.ModelAdmin): + """Модель хранения JSON данных игроков для нарезки видео.""" + + list_display = ( + "player", + "game", + "created_at", + ) + search_fields = ( + "player__name", + "game__name", + ) + list_filter = ("created_at",) diff --git a/adaptive_hockey_federation/games/apps.py b/adaptive_hockey_federation/games/apps.py new file mode 100644 index 00000000..a2928abf --- /dev/null +++ b/adaptive_hockey_federation/games/apps.py @@ -0,0 +1,15 @@ +from django.apps import AppConfig + + +class GamesConfig(AppConfig): + """Класс-конфигуратор для приложения games.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "games" + verbose_name = "Игры" + + def ready(self) -> None: + """Импортирование сигналов для приложения.""" + import games.signals # noqa + + return super().ready() diff --git a/adaptive_hockey_federation/games/constants.py b/adaptive_hockey_federation/games/constants.py new file mode 100644 index 00000000..9d4f46c8 --- /dev/null +++ b/adaptive_hockey_federation/games/constants.py @@ -0,0 +1,54 @@ +"""Константы, используемые в модуле games.""" + +from enum import IntEnum, StrEnum + +GAME_TITLE_MAPPING = dict( + GameCreateView="Создание игры", + GameEditView="Редактирование игры", +) + + +class Errors(StrEnum): + """Константы для сообщений об ошибках.""" + + NO_MORE_THAN_TWO_TEAMS_IN_GAME = ( + "В игре может участвовать не более двух команд!" + ) + MUST_BE_TWO_TEAMS_IN_A_GAME = "В игре должны участвовать две команды!" + CANNOT_PLAY_GAME_AGAINST_SELF = "Команда не может играть с самой собой!" + INCORRECT_GAME_TEAMS = "Неверный список команд!" + PERMISSION_MISSING = "Отсутствует разрешение на {action}." + EDIT_GAME = "редактирование игры" + EDIT_PLAYER_NUMBER = "редактирование номеров игроков" + CREATE_GAME = "создание игры" + DELETE_GAME = "удаление игры" + GAME_LIST_VIEW = "просмотр списка игр" + UNIQUE_PLAYER_NUMBERS_IN_TEAM = ( + "В команде должны быть уникальные номера игроков!" + ) + + +class Literals(StrEnum): + """Константы для информационных и прочих текстовых сообщений.""" + + GAME_PARTICIPATING_TEAMS = "Команды, участвующие в игре" + GAME_AVAILABLE_TEAMS = "Команды, доступные для участия в игре" + GAME_TEAMS = "Команды" + GAME_CHOSEN_TEAMS = "Выбранные команды" + GAME_FORM_DATETIME_PLACEHOLDER = "Введите дату проведения игры" + GAME_NUMBER = "Nr." + GAME_NAME = "Название" + GAME_VIDEO_LINK = "Ссылка на видео" + GAME_FIRST_TEAM = "Команда 1" + GAME_SECOND_TEAM = "Команда 2" + GAME_COMPETITION = "Соревнование" # New + + +class NumericalValues(IntEnum): + """Константы для цифровых значений.""" + + GAME_MIN_PLAYER_NUMBER = 0 + GAME_MAX_PLAYER_NUMBER = 99 + MAX_TEAMS_IN_GAME = 2 + PAGINATION_BASE_VALUE = 10 + ADMIN_EXTRA_TEAMS = 0 diff --git a/adaptive_hockey_federation/games/forms.py b/adaptive_hockey_federation/games/forms.py new file mode 100644 index 00000000..e4ba723b --- /dev/null +++ b/adaptive_hockey_federation/games/forms.py @@ -0,0 +1,220 @@ +from typing import Any + +from django import forms +from django.db import transaction +from django.db.models import Q, QuerySet +from django.forms import modelformset_factory + +from games.constants import Errors, Literals, NumericalValues +from games.models import Game, GamePlayer, GameTeam +from main.forms import CustomMultipleChoiceField +from main.models import Team + + +class CustomGameMultipleChoiceField(forms.ModelMultipleChoiceField): + """Кастомное поле выбора для команд.""" + + @staticmethod + def _check_values(value: Any) -> QuerySet: + """ + Переопределение стандартного метода проверки значений. + + Используется для обхода ограничения Django на использование + QuerySet.filter после QuerySet.difference. + """ + try: + value = frozenset(value) + except TypeError: + raise forms.ValidationError(Errors.INCORRECT_GAME_TEAMS) + value = list(value) + return Team.objects.filter(id__in=value) + + +class GameForm(forms.ModelForm): + """Форма, используемая при создании нового объекта игры.""" + + game_teams = CustomMultipleChoiceField( + label=Literals.GAME_TEAMS, + required=True, + help_text=Literals.GAME_PARTICIPATING_TEAMS, + ) + available_teams = forms.ModelMultipleChoiceField( + queryset=Team.objects.all().order_by("name"), + required=False, + help_text=Literals.GAME_AVAILABLE_TEAMS, + ) + + class Meta: + model = Game + fields = [ + "name", + "date", + "competition", + "available_teams", + "game_teams", + "video_link", + ] + widgets = { + "date": forms.DateTimeInput( + format="%Y-%m-%d %H:%M", + attrs={ + "type": "datetime-local", + "placeholder": Literals.GAME_FORM_DATETIME_PLACEHOLDER, + "class": "form-control", + }, + ), + } + + def clean_game_teams(self) -> list[Team]: + """Метод, проверяющий корректность выбора команд.""" + if ( + len(self.cleaned_data["game_teams"]) + > NumericalValues.MAX_TEAMS_IN_GAME + ): + raise forms.ValidationError(Errors.NO_MORE_THAN_TWO_TEAMS_IN_GAME) + elif ( + len(self.cleaned_data["game_teams"]) + < NumericalValues.MAX_TEAMS_IN_GAME + ): + raise forms.ValidationError(Errors.MUST_BE_TWO_TEAMS_IN_A_GAME) + if ( + self.cleaned_data["game_teams"][0] + == self.cleaned_data["game_teams"][1] + ): + raise forms.ValidationError(Errors.CANNOT_PLAY_GAME_AGAINST_SELF) + return self.cleaned_data["game_teams"] + + @transaction.atomic + def save(self, commit=True): + """ + Метод создает и сохраняет объект игры в базе данных. + + Поле game_teams удаляется из обработки, поскольку сохранение нужно + произвести не по модели Team, а по модели GameTeam. Для этой цели + id указанных Team передаются в списке через атрибут в сигналы, где + и происходит дальнейшая обработка. + """ + if isinstance(self.cleaned_data["game_teams"], list): + teams = self.cleaned_data.pop("game_teams") + else: + teams = self.cleaned_data["game_teams"].values_list( + "id", + flat=True, + ) + # Тут берём ID для того, чтобы передать их в сигнал, + # иначе всё сломается двумя строчками ниже, т.к. в случае + # редактирования формы передаётся QuerySet, а не простой список + del self.fields["game_teams"] + self.instance.teams = list(map(int, teams)) + instance = super(GameForm, self).save(commit) + return instance + + +class GameUpdateForm(GameForm): + """Форма, используемая при обновлении существующего объекта игры.""" + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + При инициализации переопределяем queryset для полей формы game_teams + и available_teams со значениями, полученными из текущей игры для + корректного отображения команд, доступны к выбору, и команд, уже + участвующих в игре. + """ + super(GameForm, self).__init__(*args, **kwargs) + if queryset := GameTeam.objects.filter(game=self.instance): + self.fields["game_teams"] = CustomGameMultipleChoiceField( + queryset=Team.objects.filter( + Q( + discipline_name__name__in=queryset.values_list( + "discipline_name", + flat=True, + ), + ) + & Q(name__in=queryset.values_list("name", flat=True)), + ), + required=True, + help_text=Literals.GAME_PARTICIPATING_TEAMS, + label=Literals.GAME_TEAMS, + ) + available_teams_qs = Team.objects.exclude( + name__in=queryset.values_list("name", flat=True), + ).order_by("name") + self.fields["available_teams"] = CustomGameMultipleChoiceField( + queryset=available_teams_qs, + required=False, + help_text=Literals.GAME_AVAILABLE_TEAMS, + ) + + +class GamePlayerNumberForm(forms.ModelForm): + """Форма для редактирования номера игрока.""" + + class Meta: + model = GamePlayer + fields = ["number"] + + +class TeamPlayersNumbersFormSet(forms.BaseModelFormSet): + """ + Формсет для валидации номеров игроков команды. + + Если игроки имеют одинаковые номера, вызывается исключение. + """ + + def clean(self): + """Метод для валидации номеров игроков команды.""" + if any(self.errors): + return + numbers = set() + for form in self.forms: + if self.can_delete and self._should_delete_form(form): + continue + number = form.cleaned_data.get("number") + if number in numbers: + raise forms.ValidationError( + Errors.UNIQUE_PLAYER_NUMBERS_IN_TEAM, + ) + numbers.add(number) + + +EditTeamPlayersNumbersFormSet = modelformset_factory( + GamePlayer, + form=GamePlayerNumberForm, + formset=TeamPlayersNumbersFormSet, + extra=0, + can_delete=True, +) + + +class EditTeamPlayersNumbersForm(forms.Form): + """Форма для редактирования номеров игроков команды.""" + + def __init__(self, *args, **kwargs): + """ + Инициализирует форму для редактирования номеров игроков команды. + + Описание: + - Извлекает объект game_team из именованных аргументов. + - Извлекает данные для заполнения формы, если они переданы. + - Инициализирует форму и формсет EditTeamPlayersNumbersFormSet + для игроков из указанной команды. + """ + self.game_team = kwargs.pop("game_team") + data = kwargs.pop("data", None) + super().__init__(*args, **kwargs) + self.formset = EditTeamPlayersNumbersFormSet( + queryset=GamePlayer.objects.filter( + game_team=self.game_team, + ).order_by("last_name", "name"), + data=data if self.is_bound else None, + ) + + def is_valid(self): + """Проверка валидности формы и формсета.""" + return super().is_valid() and self.formset.is_valid() + + def save(self): + """Сохранение изменений формсета.""" + self.formset.save() diff --git a/adaptive_hockey_federation/games/migrations/0001_initial.py b/adaptive_hockey_federation/games/migrations/0001_initial.py new file mode 100644 index 00000000..c43148f4 --- /dev/null +++ b/adaptive_hockey_federation/games/migrations/0001_initial.py @@ -0,0 +1,198 @@ +# Generated by Django 4.2.13 on 2024-06-12 10:37 + +import core.constants +import core.validators +import django.core.validators +import django.db.models.deletion +import django.db.models.functions.datetime +from django.db import migrations, models + +import games.constants +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("competitions", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Game", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + max_length=core.constants.UserConstans["NAME_MAX_LENGTH"], + verbose_name="Название игры", + ), + ), + ( + "date", + models.DateTimeField( + validators=[core.validators.validate_game_date], + verbose_name="Дата игры", + ), + ), + ( + "video_link", + models.URLField(blank=True, verbose_name="Ссылка на видео"), + ), + ( + "competition", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="competitions.competition", + verbose_name="Соревнование", + ), + ), + ], + options={ + "verbose_name": "Игра", + "verbose_name_plural": "Игры", + "ordering": ("name",), + "default_related_name": "games", + }, + ), + migrations.CreateModel( + name="GameTeam", + fields=[ + ("gameteam_id", models.BigAutoField(primary_key=True, serialize=False)), + ("id", models.IntegerField()), + ( + "name", + models.CharField( + max_length=core.constants.UserConstans["NAME_MAX_LENGTH"], + verbose_name="Название команды", + ), + ), + ( + "discipline_name", + models.CharField( + max_length=core.constants.UserConstans["NAME_MAX_LENGTH"], + verbose_name="Дисциплина", + ), + ), + ( + "game", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="games.game", + verbose_name="Игра", + ), + ), + ], + options={ + "verbose_name": "Команда, участвующая в игре", + "verbose_name_plural": "Команды, участвующие в игре", + "ordering": ("name",), + "default_related_name": "game_teams", + }, + ), + migrations.CreateModel( + name="GamePlayer", + fields=[ + ( + "gameplayer_id", + models.BigAutoField(primary_key=True, serialize=False), + ), + ("id", models.IntegerField()), + ( + "name", + models.CharField( + max_length=core.constants.UserConstans["NAME_MAX_LENGTH"], + verbose_name="Имя игрока", + ), + ), + ( + "last_name", + models.CharField( + max_length=core.constants.UserConstans["NAME_MAX_LENGTH"], + verbose_name="Фамилия игрока", + ), + ), + ( + "number", + models.PositiveSmallIntegerField( + validators=[ + django.core.validators.MinValueValidator( + games.constants.NumericalValues[ + "GAME_MIN_PLAYER_NUMBER" + ], + "Номер игрока должен быть больше или равен нулю", + ), + django.core.validators.MaxValueValidator( + games.constants.NumericalValues[ + "GAME_MAX_PLAYER_NUMBER" + ], + "Номер игрока должен быть меньше или равен 99", + ), + ], + verbose_name="Номер игрока", + ), + ), + ( + "game_team", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="games.gameteam", + verbose_name="Команда", + ), + ), + ], + options={ + "verbose_name": "Игрок, участвующий в игре", + "verbose_name_plural": "Игроки, участвующие в игре", + "default_related_name": "game_players", + }, + ), + migrations.AddConstraint( + model_name="gameplayer", + constraint=models.CheckConstraint( + check=models.Q( + ( + "number__gte", + games.constants.NumericalValues["GAME_MIN_PLAYER_NUMBER"], + ) + ), + name="player_number_must_be_positive", + ), + ), + migrations.AddConstraint( + model_name="gameplayer", + constraint=models.CheckConstraint( + check=models.Q( + ( + "number__lte", + games.constants.NumericalValues["GAME_MAX_PLAYER_NUMBER"], + ) + ), + name="player_number_must_be_99_or_less", + ), + ), + migrations.AlterUniqueTogether( + name="gameplayer", + unique_together={("name", "number", "game_team")}, + ), + migrations.AddConstraint( + model_name="game", + constraint=models.CheckConstraint( + check=models.Q( + ("date__lte", django.db.models.functions.datetime.Now()) + ), + name="game_date_must_not_be_in_the_future", + ), + ), + ] diff --git a/adaptive_hockey_federation/games/migrations/0002_gamedataplayer.py b/adaptive_hockey_federation/games/migrations/0002_gamedataplayer.py new file mode 100644 index 00000000..d77f33bc --- /dev/null +++ b/adaptive_hockey_federation/games/migrations/0002_gamedataplayer.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.13 on 2024-07-02 18:30 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("main", "0003_gamedataplayer"), + ("games", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="GameDataPlayer", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("data", models.JSONField(default=dict, verbose_name="Данные игры")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ( + "game", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="games.game" + ), + ), + ( + "player", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="main.player" + ), + ), + ], + options={ + "verbose_name": "JSON данные игрока для нарезки видео", + "verbose_name_plural": "JSON данные игроков для нарезки видео", + "default_related_name": "game_data_player", + }, + ), + ] diff --git a/adaptive_hockey_federation/games/migrations/0003_alter_gameplayer_unique_together_and_more.py b/adaptive_hockey_federation/games/migrations/0003_alter_gameplayer_unique_together_and_more.py new file mode 100644 index 00000000..13674e1a --- /dev/null +++ b/adaptive_hockey_federation/games/migrations/0003_alter_gameplayer_unique_together_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.13 on 2024-07-28 10:32 + +import core.constants +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('games', '0002_gamedataplayer'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='gameplayer', + unique_together=set(), + ), + migrations.AddField( + model_name='gameplayer', + name='patronymic', + field=models.CharField(blank=True, max_length=core.constants.UserConstans['NAME_MAX_LENGTH'], verbose_name='Отчество игрока'), + ), + migrations.AddConstraint( + model_name='gameplayer', + constraint=models.UniqueConstraint(fields=('name', 'last_name', 'patronymic', 'number', 'game_team'), name='player_number_must_be_unique'), + ), + ] diff --git a/adaptive_hockey_federation/games/migrations/__init__.py b/adaptive_hockey_federation/games/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/games/mixins.py b/adaptive_hockey_federation/games/mixins.py new file mode 100644 index 00000000..33ba1491 --- /dev/null +++ b/adaptive_hockey_federation/games/mixins.py @@ -0,0 +1,37 @@ +from typing import Any + +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.urls import reverse_lazy +from games.constants import GAME_TITLE_MAPPING, Literals +from games.models import Game + + +class GameCreateUpdateMixin(LoginRequiredMixin, PermissionRequiredMixin): + """Миксин для представлений создания и редактирования игры.""" + + model = Game + success_url = reverse_lazy("games:games") + template_name = "main/games/game_create_edit.html" + + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: + """Метод для получения словаря context в шаблоне страницы.""" + context = super(GameCreateUpdateMixin, self).get_context_data(**kwargs) + return dict( + **context, + page_title=GAME_TITLE_MAPPING[type(self).__name__], + help_text_role=Literals.GAME_CHOSEN_TEAMS, + ) + + # def get_success_url(self) -> str: + # """ + # Метод для получения URL-адреса для перенаправления по + # успешному заполнению формы. + # """ + # return reverse( + # "games:game_id", kwargs={"pk": self.object.pk} + # ) + # TODO: раскомментировать, когда появится контроллер для просмотра + # игры diff --git a/adaptive_hockey_federation/games/models.py b/adaptive_hockey_federation/games/models.py new file mode 100644 index 00000000..031a3698 --- /dev/null +++ b/adaptive_hockey_federation/games/models.py @@ -0,0 +1,185 @@ +from competitions.models import Competition +from core.constants import UserConstans +from core.validators import validate_game_date +from django.core.validators import MaxValueValidator, MinValueValidator +from django.db import models +from django.db.models.functions import Now +from django.utils.translation import gettext_lazy as _ + +from games.constants import NumericalValues +from main.models import Player + + +class Game(models.Model): + """Модель игры.""" + + name = models.CharField( + verbose_name=_("Название игры"), + max_length=UserConstans.NAME_MAX_LENGTH, + ) + date = models.DateTimeField( + verbose_name=_("Дата игры"), + validators=[ + validate_game_date, + ], + ) + competition = models.ForeignKey( + Competition, + on_delete=models.CASCADE, + verbose_name=_("Соревнование"), + ) + video_link = models.URLField(verbose_name=_("Ссылка на видео"), blank=True) + + class Meta: + default_related_name = "games" + verbose_name = "Игра" + verbose_name_plural = "Игры" + ordering = ("name",) + constraints = [ + models.CheckConstraint( + check=models.Q(date__lte=Now()), + name="game_date_must_not_be_in_the_future", + ), + ] + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class GameTeam(models.Model): + """ + Модель команды, участвующей в игре. + + В данной модели переопределено стандартное поле ID — оно заменено на + IntegerField для синхронизации ID сущностей данной модели и модели Team. + В качестве Primary Key выступает поле gameteam_id. + """ + + gameteam_id = models.BigAutoField(primary_key=True) + id = models.IntegerField() + name = models.CharField( + verbose_name=_("Название команды"), + max_length=UserConstans.NAME_MAX_LENGTH, + ) + discipline_name = models.CharField( + verbose_name=_("Дисциплина"), + max_length=UserConstans.NAME_MAX_LENGTH, + ) + game = models.ForeignKey( + Game, + on_delete=models.CASCADE, + verbose_name=_("Игра"), + ) + + class Meta: + default_related_name = "game_teams" + verbose_name = "Команда, участвующая в игре" + verbose_name_plural = "Команды, участвующие в игре" + ordering = ("name",) + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class GamePlayer(models.Model): + """ + Модель игрока, участвующего в игре. + + В данной модели переопределено стандартное поле ID — оно заменено на + IntegerField для синхронизации ID сущностей данной модели и модели Player. + В качестве Primary Key выступает поле gameplayer_id. + """ + + gameplayer_id = models.BigAutoField(primary_key=True) + id = models.IntegerField() + name = models.CharField( + verbose_name=_("Имя игрока"), + max_length=UserConstans.NAME_MAX_LENGTH, + ) + last_name = models.CharField( + verbose_name=_("Фамилия игрока"), + max_length=UserConstans.NAME_MAX_LENGTH, + ) + patronymic = models.CharField( + verbose_name=_("Отчество игрока"), + max_length=UserConstans.NAME_MAX_LENGTH, + blank=True, + ) + number = models.PositiveSmallIntegerField( + verbose_name=_("Номер игрока"), + validators=[ + MinValueValidator( + NumericalValues.GAME_MIN_PLAYER_NUMBER, + _("Номер игрока должен быть больше или равен нулю"), + ), + MaxValueValidator( + NumericalValues.GAME_MAX_PLAYER_NUMBER, + _("Номер игрока должен быть меньше или равен 99"), + ), + ], + ) + game_team = models.ForeignKey( + GameTeam, + on_delete=models.CASCADE, + verbose_name=_("Команда"), + ) + + class Meta: + default_related_name = "game_players" + verbose_name = "Игрок, участвующий в игре" + verbose_name_plural = "Игроки, участвующие в игре" + constraints = [ + models.CheckConstraint( + check=models.Q( + number__gte=NumericalValues.GAME_MIN_PLAYER_NUMBER, + ), + name="player_number_must_be_positive", + ), + models.CheckConstraint( + check=models.Q( + number__lte=NumericalValues.GAME_MAX_PLAYER_NUMBER, + ), + name=f"player_number_must_" + f"be_{NumericalValues.GAME_MAX_PLAYER_NUMBER}_or_less", + ), + models.UniqueConstraint( + name="player_number_must_be_unique", + fields=[ + "name", + "last_name", + "patronymic", + "number", + "game_team", + ], + ), + ] + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return f"{self.name} {self.last_name}" + + +class GameDataPlayer(models.Model): + """Модель хранения JSON данных игроков для нарезки видео.""" + + player = models.ForeignKey( + Player, + on_delete=models.CASCADE, + ) + game = models.ForeignKey( + Game, + on_delete=models.CASCADE, + ) + data = models.JSONField(default=dict, verbose_name=_("Данные игры")) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + verbose_name = _("JSON данные игрока для нарезки видео") + verbose_name_plural = _("JSON данные игроков для нарезки видео") + default_related_name = "game_data_player" + + def __str__(self): + """Возвращает строку, содержащую имя игрока и дату создания.""" + return f"{self.player.name} - {self.created_at}" diff --git a/adaptive_hockey_federation/games/signals.py b/adaptive_hockey_federation/games/signals.py new file mode 100644 index 00000000..f24f2631 --- /dev/null +++ b/adaptive_hockey_federation/games/signals.py @@ -0,0 +1,55 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from games.models import Game, GamePlayer, GameTeam + +from main.models import Player, Team + + +@receiver(post_save, sender=Game, dispatch_uid="unique_signal") +def create_game_teams(sender, instance, created, **kwargs): + """ + Сигнал для автоматического создания GameTeam при создании Game. + + Для последующего использования сигнала при обновлении объекта Game + реализовано удаление старых GameTeam, которые ссылались на этот Game. + """ + queryset_teams = list( + map(lambda x: Team.objects.get(id=x), instance.teams), + ) + GameTeam.objects.filter(game=instance).delete() + for team in queryset_teams: + game_team = GameTeam( + id=team.id, + name=team.name, + discipline_name=team.discipline_name.name, + game=instance, + ) + game_team.players = Player.objects.filter( + team=team, + ) + game_team.save() + + # TODO необходимо пересмотреть логику создания объекта игры, + # конкретно изменение + # номеров игроков. Оно должно происходить до того + # как объект игры попадёт в бд. + + +@receiver(post_save, sender=GameTeam, dispatch_uid="unique_signal") +def create_game_players(sender, instance, created, **kwargs): + """Сигнал для автоматического создания GamePlayer при создании GameTeam.""" + if created: + queryset_players = instance.players + all_players = [] + for player in queryset_players: + game_player = GamePlayer( + id=player.id, + name=player.name, + last_name=player.surname, + patronymic=player.patronymic, + number=player.number, + game_team=instance, + ) + all_players.append(game_player) + GamePlayer.objects.bulk_create(all_players) diff --git a/adaptive_hockey_federation/games/urls.py b/adaptive_hockey_federation/games/urls.py new file mode 100644 index 00000000..e4b0fea3 --- /dev/null +++ b/adaptive_hockey_federation/games/urls.py @@ -0,0 +1,33 @@ +from django.urls import include, path +from games import views + +app_name = "games" + +games_urlpattern = [ + path("", views.GamesListView.as_view(), name="games"), + path("create/", views.GameCreateView.as_view(), name="game_create"), + path( + "edit_numbers//", + views.EditTeamPlayersNumbersView.as_view(), + name="edit_team_players_numbers", + ), + path( + "/edit/", + views.GameEditView.as_view(), + name="game_edit", + ), + path( + "/delete/", + views.GameDeleteView.as_view(), + name="game_delete", + ), + path( + "/info-about-game/", + views.GamesInfoView.as_view(), + name="game_info", + ), +] + +urlpatterns = [ + path("games/", include(games_urlpattern)), +] diff --git a/adaptive_hockey_federation/games/views.py b/adaptive_hockey_federation/games/views.py new file mode 100644 index 00000000..ad502fb3 --- /dev/null +++ b/adaptive_hockey_federation/games/views.py @@ -0,0 +1,231 @@ +import logging +from dataclasses import dataclass + +from typing import Any +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.db.models.query import QuerySet +from django.shortcuts import get_object_or_404, redirect +from django.urls import reverse_lazy +from django.views.generic import DetailView +from django.views.generic.edit import ( + CreateView, + DeleteView, + FormView, + UpdateView, +) +from django.views.generic.list import ListView + +from games.constants import Errors, Literals, NumericalValues +from games.forms import EditTeamPlayersNumbersForm, GameForm, GameUpdateForm +from games.mixins import GameCreateUpdateMixin +from games.models import Game, GamePlayer, GameTeam +from core.logging import configure_logging + + +configure_logging() +logger = logging.getLogger(__name__) + + +@dataclass +class Message: + """Сообщение с уровнем важности.""" + + level: int + text: str + + +class GamesListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Список игр.""" + + model = Game + template_name = "main/games/games.html" + permission_required = "unloads.list_view_unload" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.GAME_LIST_VIEW, + ) + context_object_name = "games" + paginate_by = NumericalValues.PAGINATION_BASE_VALUE + ordering = ["name"] + + def get_queryset(self) -> QuerySet[Any]: + """Метод для получения набора QuerySet.""" + queryset = ( + super() + .get_queryset() + .prefetch_related("game_teams", "competition") + ) + search_params = self.request.GET.dict() + search_column = search_params.get("search_column") + search = search_params.get("search") + if search_column and search: + if search_column == "name": + queryset = queryset.filter(name__icontains=search) + elif search_column == "team": + queryset = queryset.filter(game_teams__name__icontains=search) + return queryset + + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + context["games"] = self.get_queryset() + games = context["games"] + table_data = [] + for game in games: + first_team, second_team = ( + game.game_teams.first(), + game.game_teams.last(), + ) + table_data.append( + { + "pk": game.pk, + "competition": ( + game.competition.title if game.competition else None + ), + "name": game.name, + "video_link": game.video_link, + "first_team": first_team.name if first_team else None, + "second_team": second_team.name if second_team else None, + }, + ) + context["table_head"] = { + "pk": Literals.GAME_NUMBER, + "competition": Literals.GAME_COMPETITION, + "name": Literals.GAME_NAME, + "video_link": Literals.GAME_VIDEO_LINK, + "first_team": Literals.GAME_FIRST_TEAM, + "second_team": Literals.GAME_SECOND_TEAM, + } + context["table_data"] = table_data + return context + + +class GameCreateView(GameCreateUpdateMixin, CreateView): + """Представление для создания объекта игры.""" + + form_class = GameForm + permission_required = "games.add_game" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.CREATE_GAME, + ) + + +class GameEditView(GameCreateUpdateMixin, UpdateView): + """Представление для редактирования объекта игры.""" + + form_class = GameUpdateForm + permission_required = "games.edit_game" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.EDIT_GAME, + ) + + def get_object(self, queryset: QuerySet = None) -> Game: + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Game, id=self.kwargs["game_id"]) + + +class GameDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): + """Представление для удаления объекта игры.""" + + model = Game + success_url = reverse_lazy("games:games") + template_name = "main/games/game_edit_delete_buttons.html" + + permission_required = "games.delete_game" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.DELETE_GAME, + ) + + def get_object(self, queryset: QuerySet = None) -> Game: + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Game, id=self.kwargs["game_id"]) + + +class GamesInfoView( + LoginRequiredMixin, + PermissionRequiredMixin, + DetailView, +): + """Представление для получения деталей игры.""" + + model = Game + form_class = GameForm + template_name = "main/games/game_detail.html" + permission_required = "games.delete_game" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.DELETE_GAME, + ) + + def get_object(self, queryset: QuerySet = None) -> Game: + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Game, id=self.kwargs["game_id"]) + + def get_teams(self, queryset: QuerySet = None) -> Game: + """Получить список объектов команд, связанных с игрой.""" + return GameTeam.objects.filter(game=self.kwargs["game_id"]) + + def get_context_data(self, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + + teams = self.get_teams() + + for team in teams: + players = GamePlayer.objects.filter(game_team=team) + team.players = players + + context["teams"] = teams + return context + + +class EditTeamPlayersNumbersView( + LoginRequiredMixin, + PermissionRequiredMixin, + FormView, +): + """Представление для редактирования номеров команды, участвующей в игре.""" + + template_name = "main/games/player_number_edit.html" + form_class = EditTeamPlayersNumbersForm + permission_required = "games.edit_player_number" + permission_denied_message = Errors.PERMISSION_MISSING.format( + action=Errors.CREATE_GAME, + ) + + def get_form_kwargs(self): + """Передача дополнительных аргументов в форму.""" + kwargs = super().get_form_kwargs() + game_team = get_object_or_404( + GameTeam, + gameteam_id=self.kwargs["game_team"], + ) + kwargs["game_team"] = game_team + if self.request.method == "POST": + kwargs["data"] = self.request.POST + return kwargs + + def form_valid(self, form): + """Обработка валидной формы.""" + form.save() + return redirect( + reverse_lazy( + "games:game_info", + kwargs={"game_id": form.game_team.game.id}, + ), + ) + + def get_context_data(self, **kwargs): + """Метод для получения словаря context в шаблоне страницы.""" + context = super().get_context_data(**kwargs) + context["game_team"] = get_object_or_404( + GameTeam, + gameteam_id=self.kwargs["game_team"], + ) + context["page_title"] = "Редактирование номеров игроков команды" + return context diff --git a/adaptive_hockey_federation/main/__init__.py b/adaptive_hockey_federation/main/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/main/admin/__init__.py b/adaptive_hockey_federation/main/admin/__init__.py new file mode 100644 index 00000000..f02c495c --- /dev/null +++ b/adaptive_hockey_federation/main/admin/__init__.py @@ -0,0 +1,30 @@ +from main.admin.admin import ( + CityAdmin, + DiagnosisAdmin, + DisciplineLevelAdmin, + DisciplineNameAdmin, + NosologyAdmin, + PlayerAdmin, + StaffMemberAdmin, + TeamAdmin, + admin, +) +from main.models import ( + City, + Diagnosis, + DisciplineLevel, + DisciplineName, + Nosology, + Player, + StaffMember, + Team, +) + +admin.site.register(Diagnosis, DiagnosisAdmin) +admin.site.register(DisciplineLevel, DisciplineLevelAdmin) +admin.site.register(DisciplineName, DisciplineNameAdmin) +admin.site.register(Nosology, NosologyAdmin) +admin.site.register(Player, PlayerAdmin) +admin.site.register(StaffMember, StaffMemberAdmin) +admin.site.register(Team, TeamAdmin) +admin.site.register(City, CityAdmin) diff --git a/adaptive_hockey_federation/main/admin/admin.py b/adaptive_hockey_federation/main/admin/admin.py new file mode 100644 index 00000000..c4a5794d --- /dev/null +++ b/adaptive_hockey_federation/main/admin/admin.py @@ -0,0 +1,262 @@ +from core.config.dev_settings import ADMIN_PAGE_ORDERING +from django.contrib import admin +from django.contrib.admin.sites import AdminSite +from django.http import HttpRequest +from main.admin.inlines import ( + DocumentInline, + PlayerInline, + PlayerTeamInline, + StaffTeamMemberTeamInline, +) +from main.forms import PlayerForm, TeamForm +from main.models import Diagnosis + + +class CityAdmin(admin.ModelAdmin): + """Модель городов для административной панели Django.""" + + list_display = ("pk", "name") + search_fields = ("name",) + ordering = ["name"] + + +class NosologyAdmin(admin.ModelAdmin): + """Модель нозологий для административной панели Django.""" + + list_display = ("pk", "name") + search_fields = ("name",) + ordering = ["name"] + + +class DiagnosisAdmin(admin.ModelAdmin): + """Модель диагнозов для административной панели Django.""" + + list_display = ("pk", "name", "nosology") + search_fields = ( + "pk", + "name", + "nosology__name", + ) + ordering = ["name"] + + +class DisciplineNameAdmin(admin.ModelAdmin): + """Модель дисциплин для административной панели Django.""" + + list_display = ("pk", "name") + search_fields = ("name",) + ordering = ["name"] + + +class DisciplineLevelAdmin(admin.ModelAdmin): + """Модель уровня дисциплин для административной панели Django.""" + + list_display = ("pk", "name") + search_fields = ("name",) + ordering = ["name"] + + +class DocumentAdmin(admin.ModelAdmin): + """Модель документов для административной панели Django.""" + + list_display = ("pk", "name", "file") + search_fields = ( + "name", + "file", + ) + ordering = ["name"] + + +class StaffMemberAdmin(admin.ModelAdmin): + """Модель сотрудников для административной панели Django.""" + + list_display = ("pk", "surname", "name", "patronymic", "phone") + search_fields = ( + "pk", + "surname", + "name", + "patronymic", + "phone", + ) + + +class StaffTeamMemberAdmin(admin.ModelAdmin): + """Модель сотрудников команд для административной панели Django.""" + + list_display = ( + "pk", + "staff_member", + "staff_position", + "qualification", + "notes", + ) + search_fields = ( + "pk", + "staff_member__name", + "staff_member__surname", + "staff_member__patronymic", + "staff_position", + "qualification", + "notes", + ) + + +class PlayerAdmin(admin.ModelAdmin): + """Модель игрока для административной панели Django.""" + + change_form_template = "admin/custom_change_form.html" + form = PlayerForm + list_display = ( + "pk", + "surname", + "name", + "patronymic", + "birthday", + "gender", + "get_nosology", + "diagnosis", + "discipline_name", + "discipline_level", + "level_revision", + "position", + "number", + "is_captain", + "is_assistent", + "identity_document", + ) + search_fields = ( + "pk", + "surname", + "name", + "patronymic", + "birthday", + "gender", + "diagnosis__nosology__name", + "discipline_name__name", + "discipline_level__name", + "level_revision", + "position", + "number", + "identity_document", + ) + ordering = ["surname", "name", "patronymic", "birthday"] + inlines = ( + PlayerInline, + DocumentInline, + ) + fieldsets = ( + ( + "Персональные данные", + { + "classes": ("collapse",), + "fields": ( + ( + "surname", + "name", + ), + "patronymic", + ( + "gender", + "birthday", + ), + "identity_document", + "discipline_name", + "discipline_level", + "nosology", + "diagnosis", + "level_revision", + "addition_date", + ), + }, + ), + ( + "Игровые данные", + { + "classes": ("collapse",), + "fields": ( + "position", + ( + "number", + "is_captain", + "is_assistent", + ), + ), + }, + ), + ) + readonly_fields = ("addition_date",) + list_filter = ("addition_date",) + + @admin.display( + description="Нозология", + ordering="diagnosis__nosology__name", + ) + def get_nosology(self, obj): + """Получить название нозологии.""" + return obj.diagnosis.nosology.name + + def change_view( + self, + request, + object_id, + form_url="", + extra_context=None, + ): + """Переопределяет стандартный метод change_view.""" + extra_context = extra_context or {} + extra_context["diagnoses"] = Diagnosis.objects.values_list( + "name", + flat=True, + ) + extra_context["diagnosis_name"] = self.get_object( + request, + object_id, + ).diagnosis.name + return super().change_view(request, object_id, form_url, extra_context) + + +class TeamAdmin(admin.ModelAdmin): + """Модель команд для административной панели Django.""" + + form = TeamForm + change_form_template = "admin/custom_change_form.html" + list_display = ( + "pk", + "name", + "city", + "discipline_name", + ) + search_fields = ( + "pk", + "name", + "city__name", + "discipline_name__name", + ) + ordering = ["name"] + inlines = ( + StaffTeamMemberTeamInline, + PlayerTeamInline, + ) + + +def get_app_list( + self: AdminSite, + request: HttpRequest, + app_name: str = " ", +) -> list: + app_dict = self._build_app_dict(request) + app_list = [] + + for app_name, app in app_dict.items(): + if app_name in ADMIN_PAGE_ORDERING: + app["models"].sort( + key=lambda model: ADMIN_PAGE_ORDERING[app_name].index( + model["object_name"], + ), + ) + app_list.append(app) + + return app_list + + +AdminSite.get_app_list = get_app_list diff --git a/adaptive_hockey_federation/main/admin/inlines.py b/adaptive_hockey_federation/main/admin/inlines.py new file mode 100644 index 00000000..15eeeca4 --- /dev/null +++ b/adaptive_hockey_federation/main/admin/inlines.py @@ -0,0 +1,48 @@ +from django.contrib import admin +from main.forms import PlayerTeamForm, StaffTeamMemberTeamForm +from main.models import Document, Player, StaffTeamMember + + +class PlayerInline(admin.StackedInline): + """Inline-класс для отображения игрока в админке.""" + + model = Player.team.through + form = PlayerTeamForm + insert_after = "position" + verbose_name = "Команда" + verbose_name_plural = "Участие в командах" + extra = 0 + min_num = 1 + template = "admin/custom_stacked.html" + + +class PlayerTeamInline(PlayerInline): + """Inline-класс для отображения игрока команды в админке.""" + + insert_after = "" + verbose_name = "Игрок" + verbose_name_plural = "Игроки команды" + classes = ("collapse",) + + +class DocumentInline(admin.TabularInline): + """Inline-класс для отображения документа в админке.""" + + model = Document + verbose_name = "Документ" + verbose_name_plural = "Документы" + extra = 0 + min_num = 1 + + +class StaffTeamMemberTeamInline(admin.StackedInline): + """Inline-класс для отображения сотрудника команды в админке.""" + + model = StaffTeamMember.team.through + form = StaffTeamMemberTeamForm + verbose_name = "Сотрудник" + verbose_name_plural = "Административый состав команды" + classes = ("collapse",) + extra = 0 + min_num = 1 + template = "admin/custom_stacked.html" diff --git a/adaptive_hockey_federation/main/apps.py b/adaptive_hockey_federation/main/apps.py new file mode 100644 index 00000000..78e7f4c6 --- /dev/null +++ b/adaptive_hockey_federation/main/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class MainConfig(AppConfig): + """Класс-конфигуратор для приложения main.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "main" + verbose_name = "БД игроков и команд" diff --git a/adaptive_hockey_federation/main/controllers/__init__.py b/adaptive_hockey_federation/main/controllers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/main/controllers/ajax.py b/adaptive_hockey_federation/main/controllers/ajax.py new file mode 100644 index 00000000..b639c368 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/ajax.py @@ -0,0 +1,29 @@ +from django.http import JsonResponse + +from main.models import DisciplineLevel, DisciplineName + + +def load_discipline_levels(request): + """ + Представление для получения списка уровней дисциплин по ID дисциплины. + + Используется в формах создания/редактирования данных игрока. + """ + discipline_level_id = request.GET.get("discipline_level_id") + try: + discipline_statuses = DisciplineLevel.objects.filter( + discipline_name_id=discipline_level_id, + ).all() + except ValueError: + return JsonResponse([], safe=False) + else: + return JsonResponse( + list(discipline_statuses.values("id", "name")), + safe=False, + ) + + +def filter_discipline_search(request): + """Представления для поиска, получения списка дисциплин.""" + disciplines = DisciplineName.objects.all().values("name") + return JsonResponse(list(disciplines), safe=False) diff --git a/adaptive_hockey_federation/main/controllers/main_views.py b/adaptive_hockey_federation/main/controllers/main_views.py new file mode 100644 index 00000000..4d1c8643 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/main_views.py @@ -0,0 +1,59 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.postgres.search import SearchVector +from django.views.generic.list import ListView +from main.models import Player +from main.schemas.main_schema import get_main_table_data + + +class MainView( + LoginRequiredMixin, + ListView, +): + """Main_view. Поиск игроков по фамилии/имени.""" + + model = Player + template_name = "main/home/main.html" + context_object_name = "main" + fields = [ + "id", + "surname", + "name", + "birthday", + "gender", + "number", + "discipline_name", + "discipline_level", + "diagnosis", + ] + + def get_queryset(self): + """Получить набор QuerySet.""" + query = self.request.GET.get("search") + search_vector = SearchVector("surname", "name") + queryset = None + if query: + queryset = Player.objects.annotate(search=search_vector).filter( + search=query, + ) + queryset = ( + queryset.select_related("diagnosis") + .select_related("discipline_name") + .order_by("surname") + ) + + return queryset + + def get_context_data(self, *, object_list=None, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + search = self.request.GET.get("search") + if search: + table_head = {} + for field in self.fields: + if field != "id": + table_head[field] = Player._meta.get_field( + field, + ).verbose_name + context["table_head"] = table_head + context["table_data"] = get_main_table_data(context) + return context diff --git a/adaptive_hockey_federation/main/controllers/mixins.py b/adaptive_hockey_federation/main/controllers/mixins.py new file mode 100644 index 00000000..7933130c --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/mixins.py @@ -0,0 +1,10 @@ +from main.models import Diagnosis + + +class DiagnosisListMixin: + """Миксин для использования в видах редактирования и создания команд.""" + + @staticmethod + def get_diagnosis(): + """Возвращает список диагнозов из БД.""" + return Diagnosis.objects.values_list("name", flat=True) diff --git a/adaptive_hockey_federation/main/controllers/player_views.py b/adaptive_hockey_federation/main/controllers/player_views.py new file mode 100644 index 00000000..c8670517 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/player_views.py @@ -0,0 +1,410 @@ +from typing import Any + +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.http import Http404 +from django.shortcuts import get_object_or_404, render +from django.urls import reverse, reverse_lazy +from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.list import ListView + + +from core.constants import FileConstants +from core.utils import is_uploaded_file_valid +from games.models import Game, GamePlayer +from main.controllers.mixins import DiagnosisListMixin +from main.controllers.utils import errormessage +from main.forms import PlayerForm, PlayerUpdateForm +from main.mixins import FileUploadMixin +from main.models import Player +from main.permissions import PlayerIdPermissionsMixin +from main.schemas.player_schema import ( + get_player_fields, + get_player_fields_personal, + get_player_table_data, +) +from unloads.utils import model_get_queryset + + +class PlayersListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Представление для работы со списком игроков.""" + + model = Player + template_name = "main/players/players.html" + permission_required = "main.list_view_player" + permission_denied_message = ( + "У Вас нет разрешения на просмотр списка игроков игрока." + ) + context_object_name = "players" + paginate_by = 10 + fields = [ + "id", + "surname", + "name", + "birthday", + "gender", + "number", + "discipline_name", + "discipline_level", + "team", + ] + + def get_queryset(self): + """Получить набор QuerySet.""" + queryset = super().get_queryset() + dict_param = dict(self.request.GET) + dict_param = {k: v for k, v in dict_param.items() if v != [""]} + if len(dict_param) > 1 and "search_column" in dict_param: + queryset = model_get_queryset( + "players", + Player, + dict_param, + queryset, + ) + + return ( + queryset.select_related("diagnosis") + .select_related("discipline_name") + .order_by("surname") + ) + + def get_context_data(self, *, object_list=None, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + table_head = {} + for field in self.fields: + if field != "id": + table_head[field] = Player._meta.get_field(field).verbose_name + context["table_head"] = table_head + context["table_data"] = get_player_table_data(context) + return context + + +class PlayerIDCreateView( + LoginRequiredMixin, + PlayerIdPermissionsMixin, + CreateView, + DiagnosisListMixin, + FileUploadMixin, +): + """Представление для создания нового игрока.""" + + model = Player + form_class = PlayerForm + template_name = "main/player_id/player_id_create_edit.html" + permission_required = "main.add_player" + permission_denied_message = ( + "У Вас нет разрешения на создание карточки игрока." + ) + team_id: int | None = None + + def form_valid(self, form): + """Запустить валидацию формы.""" + player = form.save() + + self.add_new_documents( + player=player, + new_files_names=self.request.POST.getlist("new_file_name[]"), + new_files_paths=self.request.FILES.getlist("new_file_path[]"), + ) + + self.delete_documents( + player=player, + deleted_files_paths=self.request.POST.getlist( + "deleted_file_path[]", + ), + ) + return super().form_valid(form) + + def get(self, request, *args, **kwargs): + """Обработчик GET-запроса.""" + self.team_id = request.GET.get("team", None) + if self.team_id is not None: + self.initial = {"team": self.team_id} + return super().get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + if self.team_id is not None: + context["team_id"] = self.team_id + context["page_title"] = "Создание профиля нового игрока" + context["diagnosis"] = self.get_diagnosis() + context["file_resolution"] = ", ".join( + ["." + res for res in FileConstants.FILE_RESOLUTION], + ) + context["help_text_role"] = "Команды игрока" + return context + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном создании.""" + if self.team_id is None: + return reverse("main:players") + else: + return reverse("main:teams_id", kwargs={"team_id": self.team_id}) + + def post(self, request, *args, **kwargs): + """Обработчик POST-запроса.""" + new_files_paths = self.request.FILES.getlist("new_file_path[]") + for file in new_files_paths: + if not is_uploaded_file_valid(file): + details = PlayerForm(request.POST) + return render( + request, + self.template_name, + {"form": details, "errormessage": errormessage}, + ) + + self.team_id = request.POST.get("team_id", None) + return super().post(request, *args, **kwargs) + + +class PlayerIdView( + LoginRequiredMixin, + PlayerIdPermissionsMixin, + DetailView, +): + """Представление для отображения отдельного игрока.""" + + model = Player + template_name = "main/player_id/player_id.html" + permission_required = "main.view_player" + permission_denied_message = ( + "У Вас нет разрешения на просмотр карточки игрока." + ) + context_object_name = "player" + + fields = [ + "surname", + "name", + "patronymic", + "gender", + "birthday", + "discipline_name", + "discipline_level", + "diagnosis", + "level_revision", + "identity_document", + "team", + "is_captain", + "is_assistent", + "position", + "number", + "document", + ] + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Player, id=self.kwargs["pk"]) + + def has_video_games(self): + """Функция для проверки наличия видео, связанных с игроком.""" + player = self.get_object() + game_player = GamePlayer.objects.filter( + name=player.name, + last_name=player.surname, + ).first() + if game_player: + games_with_video = Game.objects.filter( + game_teams__id=game_player.game_team.id, + video_link__isnull=False, + ) + return games_with_video.exists() + return False + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + player = context["player"] + player_documents = self.get_object().player_documemts.all() + context["player_fields_personal"] = get_player_fields_personal(player) + context["player_fields"] = get_player_fields(player) + context["player_documents"] = player_documents + context["has_video_games"] = self.has_video_games() + return context + + +class PlayerIDEditView( + LoginRequiredMixin, + FileUploadMixin, + PlayerIdPermissionsMixin, + DiagnosisListMixin, + UpdateView, +): + """Представление для обновления игрока.""" + + model = Player + template_name = "main/player_id/player_id_create_edit.html" + form_class = PlayerUpdateForm + permission_required = "main.change_player" + permission_denied_message = ( + "У Вас нет разрешения на изменение персональных данных игрока." + ) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse( + "main:player_id", + kwargs={ + "pk": self.object.pk, + }, + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Player, id=self.kwargs["pk"]) + + def get_initial(self): + """Добавить в словарь initial объект диагноза.""" + initial = { + "diagnosis": self.object.diagnosis.name, + } + return initial + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + player_documents = self.get_object().player_documemts.all() + context["page_title"] = "Редактирование профиля игрока" + context["player_documents"] = player_documents + context["diagnosis"] = self.get_diagnosis() + context["file_resolution"] = ", ".join( + ["." + res for res in FileConstants.FILE_RESOLUTION], + ) + context["help_text_role"] = "Команды игрока" + return context + + def form_valid(self, form): + """Запустить валидацию формы.""" + player = form.save() + self.add_new_documents( + player=player, + new_files_names=self.request.POST.getlist("new_file_name[]"), + new_files_paths=self.request.FILES.getlist("new_file_path[]"), + ) + + self.delete_documents( + player=player, + deleted_files_paths=self.request.POST.getlist( + "deleted_file_path[]", + ), + ) + + return super().form_valid(form) + + def post(self, request, *args, **kwargs): + """Обработчик POST-запросов.""" + new_files_paths = self.request.FILES.getlist("new_file_path[]") + player_documents = self.get_object().player_documemts.all() + for file in new_files_paths: + if not is_uploaded_file_valid(file): + details = PlayerForm(request.POST) + return render( + request, + self.template_name, + { + "form": details, + "errormessage": errormessage, + "player_documents": player_documents, + }, + ) + self.team_id = request.POST.get("team_id", None) + return super().post(request, *args, **kwargs) + + +class PlayerIDDeleteView( + LoginRequiredMixin, + PermissionRequiredMixin, + DeleteView, +): + """Представление для удаления игрока.""" + + model = Player + object = Player + success_url = reverse_lazy("main:players") + permission_required = "main.delete_player" + permission_denied_message = ( + "У Вас нет разрешения на удаление карточки игрока." + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Player, id=self.kwargs["pk"]) + + +class PlayerGamesVideo( + LoginRequiredMixin, + PlayerIdPermissionsMixin, + ListView, +): + """Список видео игр с участием игрока.""" + + model = Player + template_name = "main/player_id/player_id_video_games.html" + permission_required = "main.view_player" + permission_denied_message = ( + "У Вас нет разрешения на просмотр видео игр с участием игрока." + ) + context_object_name = "player" + + def get_object(self): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Player, id=self.kwargs["pk"]) + + def get_queryset(self): + """Получить набор QuerySet с играми команды игрока.""" + player = self.get_object() # Получаем объект игрока по pk из URL + game_player = GamePlayer.objects.filter( + name=player.name, + last_name=player.surname, + ).first() + + if not game_player: + raise Http404("Игрок не принимает участие в играх") + + # Фильтруем игры, в которых участвует команда игрока + games = Game.objects.filter(game_teams__id=game_player.game_team.id) + + return games + + def get_context_data(self, **kwargs) -> dict[str, Any]: + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + player_games = context["player"] + # Моковое вкрапления запроса видео моментов от менеджера + + data_key = ("pk", "name", "video_link", "__ref__") + ref_params = { + "name": "Запросить", + "type": "button", + } + table_data = [ + { + key: (ref_params if key == "__ref__" else getattr(game, key)) + for key in data_key + } + for game in player_games + ] + + context["table_head"] = { + "pk": "Nr.", + "name": "Название", + "video_link": "Ссылка на видео", + "unload_file": "Видео моменты с игроком", + } + # костыль + context["player"] = {"player_id": f'{self.kwargs["pk"]}'} + context["table_data"] = table_data + return context + + +def player_id_deleted(request): + """View для отображения информации об успешном удалении игрока.""" + return render(request, "main/player_id/player_id_deleted.html") diff --git a/adaptive_hockey_federation/main/controllers/staff_views.py b/adaptive_hockey_federation/main/controllers/staff_views.py new file mode 100644 index 00000000..f2449b64 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/staff_views.py @@ -0,0 +1,316 @@ +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.db.models import Q +from django.shortcuts import get_object_or_404 +from django.urls import reverse, reverse_lazy +from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.list import ListView +from main.forms import ( + StaffMemberForm, + StaffTeamMemberEditForm, + StaffTeamMemberForm, +) +from main.models import StaffMember, StaffTeamMember +from main.schemas.staff_schema import ( + STAFF_SEARCH_FIELDS, + add_pisition_in_context, + get_staff_fields, + get_staff_table_data, +) + + +class StaffMemberListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Представление для работы со списком сотрудников.""" + + model = StaffMember + template_name = "main/staffs/staffs.html" + permission_required = "main.list_view_staff" + permission_denied_message = ( + "Отсутствует разрешение на просмотр сотрудников." + ) + context_object_name = "staffs" + paginate_by = 10 + fields = ( + "id", + "surname", + "name", + "patronymic", + "phone", + ) + + def get_queryset(self): + """Получить набор QuerySet.""" + queryset = super().get_queryset() + search = self.request.GET.get("search") + if search: + search_column = self.request.GET.get("search_column") + if not search_column or search_column.lower() in ["все", "all"]: + or_lookup = ( + Q(surname__icontains=search) + | Q(name__icontains=search) + | Q(patronymic__icontains=search) + | Q(phone__icontains=search) + ) + queryset = queryset.filter(or_lookup) + else: + search_fields = STAFF_SEARCH_FIELDS + lookup = {f"{search_fields[search_column]}__icontains": search} + queryset = queryset.filter(**lookup) + + return queryset.order_by("surname", "name", "patronymic") + + def get_context_data(self, *, object_list=None, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + table_head = {} + for field in self.fields: + if field != "id": + table_head[field] = self.model._meta.get_field( + field, + ).verbose_name + context["table_head"] = table_head + context["table_data"] = get_staff_table_data(context) + return context + + +class StaffMemberIdView( + LoginRequiredMixin, + PermissionRequiredMixin, + DetailView, +): + """Представление для работы с отдельным сотрудником.""" + + model = StaffMember + template_name = "main/staffs/staff_id.html" + context_object_name = "staff" + fields = ( + "id", + "surname", + "name", + "patronymic", + "phone", + ) + permission_required = "main.view_staffmember" + permission_denied_message = ( + "У Вас нет разрешения на просмотр данных сотрудника." + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffMember, id=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + staff = context["staff"] + queryset = StaffTeamMember.objects.filter( + staff_member=self.kwargs["pk"], + ) + if queryset.exists(): + queryset_coach = queryset.filter(staff_position="тренер") + queryset_pusher = queryset.difference(queryset_coach) + (context["coach"], context["team_fields_coach"]) = ( + add_pisition_in_context(queryset_coach) + ) + (context["pusher"], context["team_fields_pusher"]) = ( + add_pisition_in_context(queryset_pusher) + ) + context["staff_fields"] = get_staff_fields(staff) + return context + + +class StaffMemberIdCreateView( + LoginRequiredMixin, + PermissionRequiredMixin, + CreateView, +): + """Представление создания сотрудника.""" + + model = StaffMember + form_class = StaffMemberForm + template_name = "main/staffs/staff_id_create_edit.html" + success_url = reverse_lazy("main:staffs") + permission_required = "main.add_staffmember" + permission_denied_message = "У Вас нет разрешения на создание сотрудника." + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + context["page_title"] = "Создание профиля нового сотрудника" + return context + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном создании.""" + return reverse("main:staffs") + + +class StaffMemberIdEditView( + LoginRequiredMixin, + PermissionRequiredMixin, + UpdateView, +): + """Представление редактирования сотрудника.""" + + model = StaffMember + form_class = StaffMemberForm + template_name = "main/staffs/staff_id_create_edit.html" + permission_required = "main.change_staffmember" + permission_denied_message = ( + "У Вас нет разрешения на редактирование сотрудника." + ) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse( + "main:staff_id", + kwargs={ + "pk": self.object.pk, + }, + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffMember, id=self.kwargs["pk"]) + + +class StaffMemberIdDeleteView( + LoginRequiredMixin, + PermissionRequiredMixin, + DeleteView, +): + """Представление для удаления сотрудника.""" + + model = StaffMember + object = StaffMember + success_url = reverse_lazy("main:staffs") + permission_required = "main.delete_staffmember" + permission_denied_message = "У Вас нет разрешения на удаление сотрудника." + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffMember, id=self.kwargs["pk"]) + + +class StaffMemberIdTeamCreateView( + LoginRequiredMixin, + PermissionRequiredMixin, + CreateView, +): + """Представление назначения сотрудника в команду.""" + + model = StaffTeamMember + form_class = StaffTeamMemberForm + template_name = "main/staffs/staff_id_team_edit_create.html" + permission_required = "main.change_staffteammember" + permission_denied_message = "У Вас нет разрешения на" + " редактирование сотрудника." + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + if self.kwargs["position"] == "coach": + context["page_title"] = "Добавление сотрудника в команду тренером" + else: + context["page_title"] = ( + "Добавление сотрудника в команду пушер-тьютором" + ) + context["help_text_role"] = "Команды сотрудника" + return context + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном создании.""" + return reverse( + "main:staff_id", + kwargs={ + "pk": self.get_object().pk, + }, + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffMember, id=self.kwargs["pk"]) + + def form_valid(self, form): + """Запустить валидацию формы.""" + positions = { + "coach": "тренер", + "pusher": "пушер-тьютор", + } + position = positions[self.kwargs["position"]] + if form.cleaned_data.get("staff_posistion") is None: + form.instance.staff_member = self.get_object() + form.instance.staff_position = position + return super(StaffMemberIdTeamCreateView, self).form_valid(form) + + +class StaffMemberIDTeamEditView( + LoginRequiredMixin, + PermissionRequiredMixin, + UpdateView, +): + """Представление редактирования сотрудника находящегося в команде.""" + + model = StaffTeamMember + form_class = StaffTeamMemberEditForm + template_name = "main/staffs/staff_id_team_edit_create.html" + permission_required = "main.change_staffteammember" + permission_denied_message = ( + "У Вас нет разрешения на редактирование сотрудника." + ) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse( + "main:staff_id", + kwargs={ + "pk": self.object.staff_member.pk, + }, + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffTeamMember, id=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + context["page_title"] = ( + f"Редактирование данных {self.get_object().staff_position}а" + " команды" + ) + context["on_team"] = True + context["help_text_role"] = "Команды сотрудника" + return context + + +class StaffMemberIdTeamDeleteView( + LoginRequiredMixin, + PermissionRequiredMixin, + DeleteView, +): + """Представление для удаления сотрудника из команды.""" + + model = StaffTeamMember + object = StaffTeamMember + permission_required = "main.change_staffteammember" + permission_denied_message = "У Вас нет разрешения на удаление сотрудника." + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(StaffTeamMember, id=self.kwargs["pk"]) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном удалении.""" + return reverse( + "main:staff_id", + kwargs={ + "pk": self.get_object().staff_member.pk, + }, + ) diff --git a/adaptive_hockey_federation/main/controllers/team_views.py b/adaptive_hockey_federation/main/controllers/team_views.py new file mode 100644 index 00000000..40a4ffa0 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/team_views.py @@ -0,0 +1,337 @@ +from core.constants import StaffPosition +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.db.models import Value +from django.db.models.functions import Concat +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse +from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.list import ListView +from main.forms import StaffTeamMemberAddToTeamForm, TeamFilterForm, TeamForm +from main.models import City, Player, StaffTeamMember, Team +from main.permissions import CustomPermissionMixin, TeamEditPermissionsMixin +from main.schemas.team_schema import ( + TEAM_TABLE_HEAD, + get_players_table, + get_staff_table, + get_team_table_data, +) +from unloads.utils import model_get_queryset + + +class StaffTeamMemberListMixin: + """Миксин, для выбора сотрудника команды.""" + + @staticmethod + def get_staff( + position: str | None = None, + team_to_exclude: Team | None = None, + ): + """ + Формирует кортеж из сотрудников команд (StaffTeamMember). + + Формат элемента кортежа: "Фамилия Имя Отчество, должность (ID: id)", + Если передан параметр position, то выбор фильтруется по полю + staff_position и значению этого параметра. + """ + filters = {"staff_position": position} if position else {} + exclude = {"team": team_to_exclude} if team_to_exclude else {} + query_set = StaffTeamMember.objects.filter(**filters).exclude( + **exclude, + ) + staffs = query_set.annotate( + fio=Concat( + "staff_member__surname", + Value(" "), + "staff_member__name", + Value(" "), + "staff_member__patronymic", + ), + ).values("fio", "staff_position", "id") + return tuple( + f"{i['fio']}, " f"{i['staff_position']} " f"(ID: {i['id']})" + for i in staffs + ) + + def get_coaches(self, team_to_exclude: Team | None = None): + """Получить сотрудника команды с позицией тренер.""" + return self.get_staff("тренер", team_to_exclude=team_to_exclude) + + def get_pushers(self, team_to_exclude: Team | None = None): + """Получить сотрудника команды с позицией пушер-тьютор.""" + return self.get_staff("пушер-тьютор", team_to_exclude=team_to_exclude) + + +class TeamIdView( + PermissionRequiredMixin, + DetailView, + StaffTeamMemberListMixin, +): + """ + Вид команды. + + Детальный просмотр команды по игрокам и сотрудникам. + """ + + model = Team + form_class = TeamForm + template_name = "main/teams_id/teams_id.html" + success_url = "/teams/" + permission_required = "main.view_team" + permission_denied_message = ( + "Отсутствует разрешение на просмотр карточки команды." + ) + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Team, id=self.kwargs["team_id"]) + + def update_context_with_staff_add_form( + self, + context: dict, + position_filter: str | None = None, + staff_table_index: int = 0, + data_list: tuple[str, ...] = ("",), + data_list_id: str = "available_staffs", + ): + """Обновить словарь context с помощью StaffTeamMemberAddToTeamForm.""" + new_staff_form = StaffTeamMemberAddToTeamForm( + position_filter=position_filter, + team=self.get_object(), + ) + update_data = { + "add_staff_form": new_staff_form, + "data_list": data_list, + "add_staff_field_data_list_id": data_list_id, + } + context["staff_table"][staff_table_index].update(update_data) + + def update_context_with_additional_forms(self, context): + """ + Добавляет в контекст формы добавления тренера и пушера в команду. + + Также добавляет соответствующие списки для инкрементного поиска в + поле форм. + - context: параметр по ссылке, т.е. данная переменная будет изменена в + вызывающей функции. + """ + data_list = self.get_coaches(team_to_exclude=self.object) + self.update_context_with_staff_add_form( + context, + StaffPosition.TRAINER, + 0, + data_list, + "available_coaches", + ) + + data_list = self.get_pushers(team_to_exclude=self.object) + self.update_context_with_staff_add_form( + context, + StaffPosition.OTHER, + 1, + data_list, + "available_pushers", + ) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + team = self.object + players = ( + Player.objects.filter(team=self.kwargs["team_id"]) + .select_related("diagnosis") + .select_related("discipline_name") + .all() + ) + context["players_table"] = get_players_table(players) + context["staff_table"] = get_staff_table(team) + context["team"] = team + self.update_context_with_additional_forms(context) + return context + + def post(self, request, *args, **kwargs): + """Обработка POST-запроса от форм добавления тренера или пушера.""" + form_index = int(request.POST["btn_add_staff"]) + position_filter = (StaffPosition.TRAINER, StaffPosition.OTHER)[ + form_index + ] + new_staff_form = StaffTeamMemberAddToTeamForm( + data=request.POST, + team=self.get_object(), + position_filter=position_filter, + ) + if new_staff_form.is_valid(): + staff_team_member_team = new_staff_form.save(commit=False) + staff_team_member_team.team = self.get_object() + staff_team_member_team.save() + return redirect( + "main:teams_id", + team_id=self.kwargs["team_id"], + ) + self.object = self.get_object() + context = self.get_context_data() + context["staff_table"][form_index]["add_staff_form"] = new_staff_form + return render(request, self.template_name, context) + + +class TeamListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Список спортивных команд.""" + + model = Team + template_name = "main/teams/teams.html" + permission_required = "main.list_view_team" + permission_denied_message = ( + "Отсутствует разрешение на просмотр списка команд." + ) + context_object_name = "teams" + paginate_by = 10 + ordering = ["name"] + + def get_queryset(self): + """Получить набор QuerySet.""" + queryset = super().get_queryset() + return model_get_queryset( + "teams", + Team, + dict(self.request.GET), + queryset, + ) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + teams = context["teams"] + user = self.request.user + context["form_filter"] = TeamFilterForm(self.request.GET or None) + context["table_head"] = TEAM_TABLE_HEAD + context["table_data"] = get_team_table_data(teams, user) + return context + + +class CityListMixin: + """Миксин для использования в видах редактирования и создания команд.""" + + @staticmethod + def get_cities(): + """Возвращает список имен всех городов из БД.""" + return City.objects.values_list("name", flat=True) + + +class UpdateTeamView( + LoginRequiredMixin, + TeamEditPermissionsMixin, + UpdateView, + CityListMixin, +): + """Вид с формой изменения основных данных спортивной команды.""" + + model = Team + form_class = TeamForm + template_name = "main/teams/team_create_edit.html" + success_url = "/teams/" + permission_required = "main.change_team" + permission_denied_message = "Отсутствует разрешение на изменение команд." + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + team_id = self.kwargs.get("team_id") + return get_object_or_404(Team, pk=team_id) + + def get_form_kwargs(self): + """Получить аргументы для формы.""" + kwargs = super(UpdateTeamView, self).get_form_kwargs() + kwargs.update( + initial={"city": self.object.city.name}, + user=self.request.user, + ) + return kwargs + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super(UpdateTeamView, self).get_context_data(**kwargs) + context["cities"] = self.get_cities() + context["page_title"] = "Редактирование данных команды" + return context + + +class DeleteTeamView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): + """Вид удаления спортивной команды.""" + + object = Team + model = Team + success_url = "/teams/" + permission_required = "main.delete_team" + permission_denied_message = "Отсутствует разрешение на удаление команд." + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + team_id = self.kwargs.get("team_id") + return get_object_or_404(Team, pk=team_id) + + +class CreateTeamView( + LoginRequiredMixin, + PermissionRequiredMixin, + CreateView, + CityListMixin, +): + """Вид с формой создания новой спортивной команды.""" + + model = Team + form_class = TeamForm + template_name = "main/teams/team_create_edit.html" + success_url = "/teams/" + permission_required = "main.add_team" + permission_denied_message = "Отсутствует разрешение на создание команд." + + def form_valid(self, form): + """Запустить валидацию формы.""" + form.save() + return super().form_valid(form) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super(CreateTeamView, self).get_context_data(**kwargs) + context["cities"] = self.get_cities() + context["page_title"] = "Создание команды" + return context + + +class FireStaffFromTeam( + LoginRequiredMixin, + CustomPermissionMixin, + DeleteView, +): + """Вид для удаления сотрудника из команды (связи сотрудник-команда).""" + + model = StaffTeamMember.team.through + permission_required = "main.change_team" + object = StaffTeamMember.team.through + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном удалении.""" + team_id = self.kwargs["team_id"] + return reverse("main:teams_id", args=[team_id]) + + def get_object(self): + """Получить объект по id team и staff или выбросить ошибку 404.""" + team_id = self.kwargs["team_id"] + staff_team_member_id = self.kwargs["staff_team_member_id"] + return get_object_or_404( + self.model, + team__id=team_id, + staffteammember__id=staff_team_member_id, + ) + + def test_func(self): + """Выполнить проверку разрешения на доступ к объекту.""" + team = get_object_or_404(Team, id=self.kwargs["team_id"]) + user = self.request.user + return not user.is_agent or team.curator == user diff --git a/adaptive_hockey_federation/main/controllers/utils.py b/adaptive_hockey_federation/main/controllers/utils.py new file mode 100644 index 00000000..bbe061e1 --- /dev/null +++ b/adaptive_hockey_federation/main/controllers/utils.py @@ -0,0 +1,26 @@ +from core.constants import FileConstants +from django.urls import reverse +from main.models import Player, Team + + +def get_player_href(player: Player) -> dict[str, str]: + """Возвращает словарь с информацией для ссылки на игрока.""" + url = reverse("main:player_id", args=[player.id]) + name = " ".join((player.surname, player.name)) + return {"name": name, "url": url} + + +def get_team_href(team: Team) -> dict[str, str]: + """Возвращает словарь с информацией для ссылки на команду.""" + url = reverse("main:teams_id", args=[team.id]) + name = team.name + return {"name": name, "url": url} + + +def errormessage() -> str: + return ( + "Максимальный размер файла " + + FileConstants.MAX_UPLOAD_SIZE_MB + + ", допустимые разрешения " + + ", ".join(FileConstants.FILE_RESOLUTION) + ) diff --git a/adaptive_hockey_federation/main/data_factories/__init__.py b/adaptive_hockey_federation/main/data_factories/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/main/data_factories/factories.py b/adaptive_hockey_federation/main/data_factories/factories.py new file mode 100644 index 00000000..d66c4ea9 --- /dev/null +++ b/adaptive_hockey_federation/main/data_factories/factories.py @@ -0,0 +1,309 @@ +import random +import pytz +from datetime import date, timedelta, datetime +from io import BytesIO + +import factory +from competitions.models import Competition +from django.core.files.base import File +from django.db.models import Count +from games.models import Game, GameDataPlayer +from main.models import ( + GENDER_CHOICES, + PLAYER_POSITION_CHOICES, + City, + Diagnosis, + DisciplineName, + Document, + Nosology, + Player, + StaffMember, + StaffTeamMember, + Team, +) +from PIL import Image +from users.models import User + +from .utils import check_len, get_random_objects + +SIZE_IMAGE = (100, 100) +COLOR_IMAGE = (256, 0, 0) +FORMAT_IMAGE = "RGBA" +EXT_IMAGE = "png" + + +class CityFactory(factory.django.DjangoModelFactory): + """Создания данных городов. Название города является уникальным.""" + + class Meta: + model = City + django_get_or_create = ["name"] + + name = factory.Faker("city", locale="ru_RU") + + +class StaffMemberFactory(factory.django.DjangoModelFactory): + """Создание сотрудников команд.""" + + class Meta: + model = StaffMember + + surname = factory.Faker("last_name", locale="ru_RU") + name = factory.Faker("first_name", locale="ru_RU") + patronymic = factory.Faker("middle_name", locale="ru_RU") + phone = factory.Faker("phone_number", locale="ru_RU") + + +class StaffTeamMemberFactory(factory.django.DjangoModelFactory): + """ + Создание данных о сотрудниках привязаных к командам. + + Квалификация может быть "Тренер" и "Другие сотрудники". + """ + + class Meta: + model = StaffTeamMember + skip_postgeneration_save = True + + staff_member = factory.SubFactory(StaffMemberFactory) + qualification = factory.Faker( + "sentence", + nb_words=5, + locale="ru_RU", + ) + notes = factory.Faker("sentence", nb_words=10, locale="ru_RU") + + @factory.post_generation + def check_field(self, create, extracted, **kwargs): + """Метод для проверки полей: qualification и notes.""" + qualification = self.qualification + notes = self.notes + if create: + self.qualification = check_len(qualification, 5, 3) + self.notes = check_len(notes, 10, 7) + + @factory.post_generation + def team(self, create, extracted, **kwargs): + """Метод для создания связи со случайными командами.""" + if create: + self.team.set([get_random_objects(Team)]) + + +class NosologyFactory(factory.django.DjangoModelFactory): + """Создание нозологий.""" + + class Meta: + model = Nosology + skip_postgeneration_save = True + + name = factory.Faker("sentence", nb_words=5, locale="ru_RU") + diagnosis = factory.RelatedFactoryList( + "main.data_factories.factories.DiagnosisFactory", + factory_related_name="nosology", + size=lambda: random.randint( + 3, + 5, + ), + ) + + @factory.post_generation + def check_field(self, create, extracted, **kwargs): + """Метод для провеки поля name.""" + field = self.name + if create: + self.name = check_len(field, 5, 3) + + +class DiagnosisFactory(factory.django.DjangoModelFactory): + """ + Создание диагнозов, и связанных с ними нозологий. + + Колонка "name" является уникальной. + """ + + class Meta: + model = Diagnosis + django_get_or_create = ["name"] + skip_postgeneration_save = True + + nosology = factory.SubFactory(NosologyFactory) + name = factory.Faker("sentence", nb_words=5, locale="ru_RU") + + @factory.post_generation + def check_field(self, create, extracted, **kwargs): + """Метод для провеки поля name.""" + field = self.name + if create: + self.name = check_len(field, 5, 3) + + +class TeamFactory(factory.django.DjangoModelFactory): + """ + Создание команд. + + Привязка к ним уже созданных городов, сотрудников(тренеров), кураторов, + дисциплин. Колонка с названием команды является уникальной. + """ + + class Meta: + model = Team + django_get_or_create = ["name"] + + name = factory.Faker("sentence", nb_words=2, locale="ru_RU") + city = factory.SubFactory(CityFactory) + + @factory.lazy_attribute + def discipline_name(self): + """Получить случайный набор дисциплин.""" + return get_random_objects(DisciplineName) + + @factory.lazy_attribute + def curator(self): + """Получить случайный набор кураторов.""" + return get_random_objects(User) + + +class CompetitionFactory(factory.django.DjangoModelFactory): + """ + Создание соревнований. + + Привязка к ним уже созданных городов, команд создание локации, + времени начала и окончания, активно или закончено. + """ + + class Meta: + model = Competition + skip_postgeneration_save = True + django_get_or_create = ["title"] + + title = factory.Faker("sentence", nb_words=2, locale="ru_RU") + city = factory.SubFactory(CityFactory) + date_start = date.today() + timedelta(days=random.randrange(5, 30, 5)) + date_end = date_start + timedelta(days=random.randrange(2, 10, 2)) + location = factory.Faker("sentence", nb_words=4, locale="ru_RU") + + @factory.post_generation + def teams(self, create, extracted, **kwargs): + """Добавляет команды к объекту соревнования.""" + if create: + teams = Team.objects.all() + list_teams = random.choices(teams, k=8) + for team in list_teams: + self.teams.add(team) + + @factory.post_generation + def disciplines(self, create, extracted, **kwargs): + """Добавляет дисциплины к объекту соревнования.""" + if create: + disciplines = DisciplineName.objects.all() + list_disciplines = random.choices(disciplines, k=4) + for discipline in list_disciplines: + self.disciplines.add(discipline) + + +class PlayerFactory(factory.django.DjangoModelFactory): + """Фабрика для создания игрока.""" + + class Meta: + model = Player + django_get_or_create = ["birthday"] + skip_postgeneration_save = True + + surname = factory.Faker("last_name", locale="ru_RU") + name = factory.Faker("first_name", locale="ru_RU") + patronymic = factory.Faker("middle_name", locale="ru_RU") + birthday = factory.Faker("date_of_birth", minimum_age=12, maximum_age=18) + addition_date = factory.Faker( + "date_time_this_decade", + before_now=True, + after_now=False, + ) + gender = factory.LazyFunction(lambda: random.choice(GENDER_CHOICES)[1]) + level_revision = factory.Faker("sentence", nb_words=1, locale="ru_RU") + position = factory.LazyFunction( + lambda: random.choice(PLAYER_POSITION_CHOICES)[1], + ) + number = factory.Faker("random_number", digits=2) + identity_document = factory.LazyFunction( + lambda: random.choice(["паспорт", "свидетельство"]), + ) + + @factory.lazy_attribute + def diagnosis(self): + """Получить случайный набор диагнозов.""" + return get_random_objects(Diagnosis) + + @factory.lazy_attribute + def discipline_name(self): + """Получить случайный набор дисциплин.""" + return get_random_objects(DisciplineName) + + @factory.post_generation + def team(self, create, extracted, **kwargs): + """Добавляет команды к объекту игрока.""" + if create: + teams_with_player_count = Team.objects.annotate( + player_count=Count("team_players"), + ) + if teams_with_player_count.filter(player_count__lt=10): + self.team.set([get_random_objects(Team)]) + + +class DocumentFactory(factory.django.DjangoModelFactory): + """Фабрика для создания документов.""" + + class Meta: + model = Document + skip_postgeneration_save = True + + name = factory.LazyAttribute( + lambda obj: f"{obj.player.surname}-{random.randint(1000, 9999)}", + ) + + @factory.lazy_attribute + def player(self): + """Получить случайный набор игроков.""" + return get_random_objects(PlayerFactory) + + @factory.post_generation + def file(self, create, extracted, **kwargs): + """Сохранить файл в документе.""" + if not create: + return + file_obj = BytesIO() + image = Image.new(FORMAT_IMAGE, size=SIZE_IMAGE, color=COLOR_IMAGE) + image.save(file_obj, EXT_IMAGE) + file_obj.seek(0) + self.file.save(f"{self.name}.png", File(file_obj)) + + +class GameFactory(factory.django.DjangoModelFactory): + """Фабрика для создания игр.""" + + class Meta: + model = Game + skip_postgeneration_save = True + + name = factory.Faker("sentence", locale="ru_RU") + date = factory.LazyFunction( + lambda: datetime.now(pytz.timezone("Europe/Moscow")), + ) + video_link = factory.Faker("url") + competition = factory.SubFactory(CompetitionFactory) + + +class GameDataPlayerFactory(factory.django.DjangoModelFactory): + """Фабрика для создания данных JSON игрока.""" + + class Meta: + model = GameDataPlayer + + player = factory.SubFactory(PlayerFactory) + game = factory.SubFactory(GameFactory) + data = factory.LazyFunction( + lambda: { + "game_link": "https://disk.yandex.ru/i/JLh__1IbAfmK-Q", + "player_number": random.randint(1, 99), + "frames": [random.randint(5000, 10000) for _ in range(3)], + }, + ) diff --git a/adaptive_hockey_federation/main/data_factories/utils.py b/adaptive_hockey_federation/main/data_factories/utils.py new file mode 100644 index 00000000..42965a05 --- /dev/null +++ b/adaptive_hockey_federation/main/data_factories/utils.py @@ -0,0 +1,58 @@ +import random + +from main.models import DisciplineLevel, DisciplineName, Player, Team + + +def check_len(field, max, min): + """ + Функция проверяет количество созданных фабрикой слов. + + При необходимости коректирует их число до требуемого. + """ + words = field.split() + count = min - len(words) + if len(words) < min: + add_words = words[:count] + words.append(" ".join(add_words)) + if len(words) > max: + del words[max:] + field = " ".join(words) + return field + + +def get_random_objects(model): + """Функция получает рандомные записи, из представленой модели данных.""" + queryset = model.objects.distinct() + return random.choice(queryset) + + +def updates_for_players(): + """ + Обновления записей игроков в базе данных. + + Функция проходит по всем существующим командам, присваивает должности + капитанов и помощников, в каждой команде по одному капитану и помощнику. + Затем к каждому игроку в команде присваивается дисциплина, + которая соответствует его команде. + """ + teams = Team.objects.all() + for team in teams: + player_in_team = Player.objects.filter(team__id=team.id) + disciplines_names = DisciplineName.objects.filter( + id=team.discipline_name.id, + ) + discipline_name = random.choice(disciplines_names) + discipline_levels = DisciplineLevel.objects.filter( + discipline_name=discipline_name, + ) + discipline_level = random.choice(discipline_levels) + captain = random.choice(player_in_team) + assistent = random.choice(player_in_team) + captain.is_captain = True + assistent.is_assistent = True + captain.save() + assistent.save() + player_in_team.update( + discipline_name=discipline_name, + discipline_level=discipline_level, + ) diff --git a/adaptive_hockey_federation/main/fields.py b/adaptive_hockey_federation/main/fields.py new file mode 100644 index 00000000..b27b6396 --- /dev/null +++ b/adaptive_hockey_federation/main/fields.py @@ -0,0 +1,171 @@ +import re +from typing import Any + +from django.core.exceptions import ValidationError +from django.forms import ( + ModelChoiceField, + ModelMultipleChoiceField, + MultipleChoiceField, + TextInput, +) +from django.shortcuts import get_object_or_404 + +from main.models import City, Diagnosis, StaffTeamMember, Team + + +class CustomMultipleChoiceField(MultipleChoiceField): + """Класс для расширения множественного поля выбора.""" + + def validate(self, value): + """Метод для валидации обязательного значения.""" + if self.required and not value: + raise ValidationError( + self.error_messages["required"], + code="required", + ) + + +class CustomModelMultipleChoiceField(ModelMultipleChoiceField): + """Класс для расширения множественного поля выбора модели.""" + + def _check_values(self, value): + """Метод, проверяющий правильно ли указан список команд.""" + try: + value = frozenset(value) + except TypeError: + raise ValidationError("Неверный список команд!") + value = list(value) + qs = Team.objects.filter(pk__in=value) + return qs + + +class CustomDiagnosisChoiceField(ModelChoiceField): + """Самодельное поле для ввода диагноза.""" + + def __init__(self, label: str | None = None): + """ + Метод инициализации экземпляра класса. + + Добавляет виджет к полю выбора для поиска диагноза по названию. + """ + super().__init__( + queryset=Diagnosis.objects.all(), + required=True, + widget=TextInput( + attrs={ + "list": "diagnosis", + "placeholder": "Введите название диагноза", + }, + ), + error_messages={ + "required": "Пожалуйста, выберите диагноз из списка.", + }, + label=label or "Выберите диагноз", + ) + + def clean(self, value: Any) -> Any: + """ + Метод валидации поля. + + Прежде, чем вызвать родительский метод, получает объект диагноза + (Diagnosis) по введенному названию, проверяет наличие введенного + наименования диагноза в БД. + """ + if not value: + raise ValidationError(self.error_messages["required"]) + return value + + +class CityChoiceField(ModelChoiceField): + """Самодельное поле для выбора города.""" + + def __init__(self, label: str | None = None): + """ + Метод инициализации экземпляра класса. + + Добавляет виджет к полю выбора для поиска города по названию. + """ + super().__init__( + queryset=City.objects.all(), + widget=TextInput( + attrs={ + "list": "cities", + "placeholder": "Введите или выберите название города", + }, + ), + required=True, + error_messages={ + "required": "Пожалуйста, выберите город из списка.", + }, + label=label or "Выберите город", + ) + + def clean(self, value: Any) -> Any: + """ + Переопределенный метод родительского класса. + + Прежде, чем вызвать родительский метод, получает объект города ( + City) по введенному названию, проверяет наличие введенного + наименования города в БД. Если такого города в БД нет, то создает + соответствующий город (объект класса City) и возвращает его на + дальнейшую стандартную валидацию формы. + """ + if not value: + raise ValidationError(self.error_messages["required"]) + + if value.isdigit(): + return super().clean(value) + else: + city, created = City.objects.get_or_create(name=value) + return city + + +class StaffTeamMemberChoiceField(ModelChoiceField): + """Самодельное поле выбора сотрудника команды.""" + + def __init__(self, team: Team, data_list: str, label: str | None = None): + """ + Метод инициализации экземпляра класса. + + Добавляет виджет к полю выбора для поиска сотрудника команды. + """ + super().__init__( + queryset=StaffTeamMember.objects.all(), + widget=TextInput( + attrs={ + "list": data_list, + "placeholder": "Начните ввод и выберите из списка", + }, + ), + required=True, + error_messages={ + "required": "Пожалуйста, выберите сотрудника из списка.", + }, + label=label or "Выберите сотрудника", + ) + self.team = team + + def clean(self, value: Any) -> Any: + """ + Переопределенный метод родительского класса. + + Прежде, чем вызвать родительский метод, получает объект + StaffTeamMember и возвращает его на + дальнейшую стандартную валидацию формы. + """ + if not value: + raise ValidationError(self.error_messages["required"]) + + if value.isdigit(): + value = value + elif m := re.search(r"\d+\)\Z", value): + value = int(m.group()[:-1]) + else: + raise ValidationError("Неверный формат введенных данных.") + staff_team_member = get_object_or_404(StaffTeamMember, id=value) + if StaffTeamMember.team.through.objects.filter( + staffteammember=staff_team_member, + team=self.team, + ).exists(): + raise ValidationError("Этот сотрудник уже есть в команде.") + return super().clean(value) diff --git a/adaptive_hockey_federation/main/forms.py b/adaptive_hockey_federation/main/forms.py new file mode 100644 index 00000000..f4baeedb --- /dev/null +++ b/adaptive_hockey_federation/main/forms.py @@ -0,0 +1,440 @@ +import re + +from django import forms +from django.core.exceptions import ValidationError +from django.forms import ( + ModelChoiceField, + ModelMultipleChoiceField, + Select, + TextInput, +) + +from core.constants import FORM_HELP_TEXTS, Role, StaffPosition +from core.utils import max_date, min_date +from main.fields import ( + CityChoiceField, + CustomDiagnosisChoiceField, + CustomModelMultipleChoiceField, + CustomMultipleChoiceField, + StaffTeamMemberChoiceField, +) +from main.models import ( + City, + Diagnosis, + DisciplineName, + Nosology, + Player, + StaffMember, + StaffTeamMember, + Team, +) +from users.models import User + + +class PlayerForm(forms.ModelForm): + """Форма для игрока.""" + + nosology = ModelChoiceField( + queryset=Nosology.objects.all(), + required=True, + error_messages={ + "required": "Пожалуйста, выберите нозологию из списка.", + }, + label="Нозология", + ) + + diagnosis = CustomDiagnosisChoiceField() + + team = CustomModelMultipleChoiceField( + queryset=None, + required=True, + help_text=FORM_HELP_TEXTS["player_teams"], + label="Команды", + ) + available_teams = CustomModelMultipleChoiceField( + queryset=None, + required=False, + help_text=FORM_HELP_TEXTS["available_teams"], + label="Команды", + ) + + class Meta: + model = Player + fields = [ + "surname", + "name", + "patronymic", + "gender", + "birthday", + "identity_document", + "discipline_name", + "discipline_level", + "nosology", + "diagnosis", + "level_revision", + "is_captain", + "is_assistent", + "position", + "number", + ] + widgets = { + "surname": forms.TextInput( + attrs={"placeholder": "Введите фамилию"}, + ), + "name": forms.TextInput(attrs={"placeholder": "Введите Имя"}), + "patronymic": forms.TextInput( + attrs={"placeholder": "Введите отчество"}, + ), + "identity_document": forms.TextInput( + attrs={"placeholder": "Введите название документа"}, + ), + "number": forms.TextInput( + attrs={"placeholder": "Введите номер игрока"}, + ), + "level_revision": forms.TextInput( + attrs={"placeholder": "Введите игровую классификацию"}, + ), + "birthday": forms.DateInput( + format="%Y-%m-%d", + attrs={ + "type": "date", + "placeholder": "Введите дату рождения", + "class": "form-control", + "min": min_date, + "max": max_date, + }, + ), + } + help_texts = { + "identity_document": FORM_HELP_TEXTS["identity_document"], + "birthday": FORM_HELP_TEXTS["birthday"], + } + + def __init__(self, *args, **kwargs): + """Инициализация формы для игрока.""" + super().__init__(*args, **kwargs) + self.fields["team"].queryset = Team.objects.none() + self.fields[ + "available_teams" + ].queryset = Team.objects.all().prefetch_related("city") + + def save(self, commit=True): + """Метод создает и сохраняет объект игрока в базе данных.""" + instance = super().save(commit=False) + if commit: + instance.save() + instance.team.set(self.cleaned_data["team"]) + return instance + + def clean_identity_document(self): + """Метод, выполняющий валидацию документа, удостоверяющего личность.""" + document = self.cleaned_data["identity_document"] + if re.search(r"[П|п]аспорт", document) or re.search( + r"[С|с]видетельство о рождении", + document, + ): + return document + raise ValidationError( + "Введите данные в формате 'Паспорт ХХХХ ХХХХХХ' или" + "'Свидетельство о рождении X-XX XXXXXX'", + ) + + def clean_diagnosis(self): + """Метод, выполняющий валидацию поля с диагнозом.""" + nosology = self.cleaned_data.get("nosology") + diagnosis = self.cleaned_data.get("diagnosis") + if Diagnosis.objects.filter(name=diagnosis).exists(): + diagnos = Diagnosis.objects.get(name=diagnosis) + if diagnos.nosology != nosology: + diagnos.nosology = nosology + diagnos.save() + return diagnos + diagnos = Diagnosis.objects.create(name=diagnosis, nosology=nosology) + return diagnos + + +class PlayerUpdateForm(PlayerForm): + """Форма для обновления игрока.""" + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + Расширяет team и available_teams кастомными полями выбора. + """ + super(PlayerUpdateForm, self).__init__(*args, **kwargs) + queryset = self.instance.team.all().prefetch_related("city") + self.fields["team"].queryset = queryset + self.fields["available_teams"].queryset = ( + Team.objects.all().prefetch_related("city").difference(queryset) + ) + self.fields["nosology"].initial = self.instance.diagnosis.nosology + self.fields["diagnosis"].initial = self.instance.diagnosis + + def clean_number(self): + """ + Метод, выполняющий валидацию номера игрока. + + Если номер игрока уже присвоен другому игроку в выбранной команде, + то выбрасывается исключение. + """ + number = self.cleaned_data["number"] + selected_teams = self.cleaned_data.get( + "team", + self.instance.team.all(), + ) + + if ( + Player.objects.filter(number=number, team__in=selected_teams) + .exclude(pk=self.instance.pk) + .exists() + ): + raise ValidationError( + "Этот номер уже используется другим игроком " + "в выбранной команде.", + ) + + return number + + def save(self, commit=True): + """ + Метод сохраняет объект игрока в базе данных. + + Сохраняется информация о командах игрока. + """ + instance = super().save(commit=False) + if commit: + instance.save() + instance.team.through.objects.filter( + team__in=self.cleaned_data["team"], + ).delete() + instance.team.set(self.cleaned_data["team"]) + return instance + + +class TeamForm(forms.ModelForm): + """Форма для команды.""" + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + self.user: User | None = kwargs.pop("user", None) + super(TeamForm, self).__init__(*args, **kwargs) + self.fields["curator"].label_from_instance = ( + lambda obj: obj.get_full_name() + ) + if self.user: + self.fields["curator"].disabled = self.user.is_agent + + city = CityChoiceField() + + curator = ModelChoiceField( + queryset=User.objects.filter(role=Role.AGENT), + required=True, + widget=forms.Select(attrs={"class": "form-control"}), + label="Куратор команды", + empty_label="Выберите куратора", + error_messages={ + "required": "Пожалуйста, выберите куратора из списка.", + }, + ) + discipline_name = forms.ModelChoiceField( + queryset=DisciplineName.objects.all(), + required=True, + widget=forms.Select(attrs={"class": "form-control"}), + label="Дисциплина команды", + empty_label="Выберите дисциплину команды", + error_messages={ + "required": "Пожалуйста, выберите дисциплину из списка.", + }, + ) + + class Meta: + model = Team + fields = ["name", "city", "discipline_name", "curator"] + widgets = { + "name": TextInput( + attrs={"placeholder": "Введите название команды"}, + ), + "staff_team_member": Select(), + "discipline_name": Select(), + "curator": Select(), + } + + def save(self, commit=True): + """Метод создает и сохраняет объект команды в базе данных.""" + instance = super(TeamForm, self).save(commit=False) + if commit: + instance.save() + return instance + + +class TeamFilterForm(forms.Form): + """Форма для фильтрации команд.""" + + name = forms.CharField( + required=False, + label="Команда", + widget=forms.TextInput(attrs={"class": "form-control arrow-before"}), + ) + discipline = forms.ModelChoiceField( + queryset=DisciplineName.objects.all().order_by("name"), + required=False, + label="Дисциплина", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + empty_label="Все", + ) + city = forms.ModelChoiceField( + queryset=City.objects.all().order_by("name"), + required=False, + label="Город", + widget=forms.Select(attrs={"class": "form-control arrow-before"}), + empty_label="Все", + ) + + class Meta: + fields = ("name", "discipline", "city") + + +class PlayerTeamForm(forms.ModelForm): + """Форма для игроков и команд.""" + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + super(PlayerTeamForm, self).__init__(*args, **kwargs) + self.fields["player"].label_from_instance = ( + lambda obj: obj.get_name_and_position() + ) + + class Meta: + labels = { + "player": "Игрок", + "team": "Название команды", + } + + +class StaffTeamMemberTeamForm(forms.ModelForm): + """Форма для сотрудников и команд.""" + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + super(StaffTeamMemberTeamForm, self).__init__(*args, **kwargs) + self.fields["staffteammember"].label_from_instance = ( + lambda obj: obj.get_name_and_staff_position() + ) + + class Meta: + labels = { + "staffteammember": "Сотрудник команды", + "team": "Команда", + } + + +class StaffTeamMemberForm(forms.ModelForm): + """Форма для сотрудников команд.""" + + available_teams = ModelMultipleChoiceField( + queryset=Team.objects.all().order_by("name"), + required=False, + help_text=FORM_HELP_TEXTS["available_teams"], + label="Команды", + ) + + team = CustomMultipleChoiceField( + required=True, + help_text=FORM_HELP_TEXTS["staff_teams"], + label="Команды", + ) + + class Meta: + model = StaffTeamMember + fields = ( + "available_teams", + "team", + "qualification", + "notes", + ) + help_texts = { + "team": FORM_HELP_TEXTS["staff_teams"], + } + + +class StaffTeamMemberEditForm(StaffTeamMemberForm): + """Форма для обновления сотрудников команд.""" + + def __init__(self, *args, **kwargs): + """ + Метод инициализации экземпляра класса. + + Расширяет team и available_teams кастомными полями выбора. + """ + super(StaffTeamMemberForm, self).__init__(*args, **kwargs) + if queryset := self.instance.team.all(): + self.fields["team"] = CustomModelMultipleChoiceField( + queryset=queryset, + required=True, + help_text=FORM_HELP_TEXTS["staff_teams"], + label="Команды", + ) + queryset_available = Team.objects.all().difference(queryset) + self.fields["available_teams"] = CustomModelMultipleChoiceField( + queryset=queryset_available, + required=False, + help_text=FORM_HELP_TEXTS["available_teams"], + label="Команды", + ) + + +class StaffMemberForm(forms.ModelForm): + """Форма для сотрудников.""" + + class Meta: + model = StaffMember + fields = ( + "surname", + "name", + "patronymic", + "phone", + ) + widgets = { + "surname": forms.TextInput( + attrs={"placeholder": "Введите фамилию"}, + ), + "name": forms.TextInput(attrs={"placeholder": "Введите Имя"}), + "patronymic": forms.TextInput( + attrs={"placeholder": "Введите отчество"}, + ), + "phone": forms.TextInput( + attrs={"placeholder": "Введите номер телефона"}, + ), + } + + +class StaffTeamMemberAddToTeamForm(forms.ModelForm): + """Форма для добавления сотрудника в команду.""" + + def __init__(self, position_filter: str | None = None, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + self.team = kwargs.pop("team") + data_list_dict = { + StaffPosition.TRAINER: "available_coaches", + StaffPosition.OTHER: "available_pushers", + "None": "available_staffs", + } + self.position_filter = position_filter or "None" + self.data_list_id = data_list_dict[self.position_filter] + super(StaffTeamMemberAddToTeamForm, self).__init__(*args, **kwargs) + self.fields["staffteammember"] = StaffTeamMemberChoiceField( + team=self.team, + data_list=self.data_list_id, + ) + + class Meta: + model = StaffTeamMember.team.through + fields = ("staffteammember",) + + def save(self, commit=True): + """Метод создает и сохраняет объект в базе данных.""" + instance = super(StaffTeamMemberAddToTeamForm, self).save(commit=False) + if commit: + instance.save() + return instance diff --git a/adaptive_hockey_federation/main/migrations/0001_initial.py b/adaptive_hockey_federation/main/migrations/0001_initial.py new file mode 100644 index 00000000..f8c47db8 --- /dev/null +++ b/adaptive_hockey_federation/main/migrations/0001_initial.py @@ -0,0 +1,218 @@ +# Generated by Django 4.2.13 on 2024-06-03 19:22 + +import core.constants +import core.validators +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +import phonenumber_field.modelfields +import phonenumber_field.validators +import users.validators +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='City', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, verbose_name='Наименование')), + ], + options={ + 'verbose_name': 'Город', + 'verbose_name_plural': 'Города', + }, + ), + migrations.CreateModel( + name='Diagnosis', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, verbose_name='Наименование')), + ], + options={ + 'verbose_name': 'Диагноз', + 'verbose_name_plural': 'Диагнозы', + }, + ), + migrations.CreateModel( + name='DisciplineLevel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Наименование')), + ], + options={ + 'verbose_name': 'Классификация/статус дисциплины', + 'verbose_name_plural': 'Классификация/статусы дисциплин', + }, + ), + migrations.CreateModel( + name='DisciplineName', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, verbose_name='Наименование')), + ], + options={ + 'verbose_name': 'Название дисциплины', + 'verbose_name_plural': 'Названия дисциплин', + }, + ), + migrations.CreateModel( + name='Document', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Наименование')), + ('file', models.FileField(max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, upload_to='players_documents')), + ], + options={ + 'verbose_name': 'Документ', + 'verbose_name_plural': 'Документы', + }, + ), + migrations.CreateModel( + name='Nosology', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, verbose_name='Наименование')), + ], + options={ + 'verbose_name': 'Нозология', + 'verbose_name_plural': 'Нозология', + }, + ), + migrations.CreateModel( + name='Player', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('surname', models.CharField(default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Фамилия', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Фамилия')), + ('name', models.CharField(default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Имя', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Имя')), + ('patronymic', models.CharField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Отчество', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Отчество')), + ('birthday', models.DateField(help_text='Дата рождения', validators=[core.validators.validate_date_birth], verbose_name='Дата рождения')), + ('addition_date', models.DateField(default=django.utils.timezone.now, help_text='Дата добавления в базу данных', verbose_name='Дата добавления')), + ('gender', models.CharField(choices=[('Мужской', 'Мужской'), ('Женский', 'Женский')], default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Пол', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Пол')), + ('level_revision', models.TextField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Игровая классификация', verbose_name='Игровая классификация')), + ('position', models.CharField(choices=[('Нападающий', 'Нападающий'), ('Поплавок', 'Поплавок'), ('Вратарь', 'Вратарь'), ('Защитник', 'Защитник')], default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Игровая позиция', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Игровая позиция')), + ('number', models.IntegerField(default=core.constants.MainConstantsInt['DEFAULT_VALUE'], help_text='Номер игрока', verbose_name='Номер игрока')), + ('is_captain', models.BooleanField(default=False, verbose_name='Капитан')), + ('is_assistent', models.BooleanField(default=False, verbose_name='Ассистент')), + ('identity_document', models.TextField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Удостоверение личности', verbose_name='Удостоверение личности')), + ], + options={ + 'verbose_name': 'Игрок', + 'verbose_name_plural': 'Игроки', + 'ordering': ('surname', 'name', 'patronymic'), + 'permissions': [('list_view_player', 'Can view list of Игрок')], + 'abstract': False, + 'default_related_name': 'players', + }, + ), + migrations.CreateModel( + name='StaffMember', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('surname', models.CharField(default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Фамилия', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Фамилия')), + ('name', models.CharField(default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Имя', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Имя')), + ('patronymic', models.CharField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Отчество', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Отчество')), + ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ', max_length=128, region=None, validators=[phonenumber_field.validators.validate_international_phonenumber, users.validators.zone_code_without_seven_hundred], verbose_name='Актуальный номер телефона')), + ], + options={ + 'verbose_name': 'Сотрудник', + 'verbose_name_plural': 'Сотрудники', + }, + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Наименование', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], unique=True, verbose_name='Наименование')), + ('city', models.ForeignKey(help_text='Город откуда команда', on_delete=django.db.models.deletion.CASCADE, to='main.city', verbose_name='Город откуда команда')), + ('curator', models.ForeignKey(help_text='Куратор команды', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team', to=settings.AUTH_USER_MODEL, verbose_name='Куратор команды')), + ('discipline_name', models.ForeignKey(help_text='Дисциплина команды', on_delete=django.db.models.deletion.CASCADE, to='main.disciplinename', verbose_name='Дисциплина команды')), + ], + options={ + 'verbose_name': 'Команда', + 'verbose_name_plural': 'Команды', + 'permissions': [('list_view_team', 'Can view list of Команда')], + 'default_related_name': 'teams', + }, + ), + migrations.CreateModel( + name='StaffTeamMember', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('staff_position', models.CharField(choices=[('тренер', 'тренер'), ('пушер-тьютор', 'пушер-тьютор')], default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Статус сотрудника', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Статус сотрудника')), + ('qualification', models.CharField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Квалификация', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Квалификация')), + ('notes', models.TextField(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Описание', verbose_name='Описание')), + ('staff_member', models.ForeignKey(help_text='Сотрудник', on_delete=django.db.models.deletion.CASCADE, to='main.staffmember', verbose_name='Сотрудник')), + ('team', models.ManyToManyField(blank=True, default='Свободный агент', help_text='Команда', related_name='team_members', to='main.team', verbose_name='Команда')), + ], + options={ + 'verbose_name': 'Сотрудник команды', + 'verbose_name_plural': 'Сотрудники команды', + 'permissions': [('list_view_staff', 'Can view list of Персонала команды')], + }, + ), + migrations.AddConstraint( + model_name='staffmember', + constraint=models.UniqueConstraint(fields=('name', 'surname', 'patronymic'), name='staff_member_unique'), + ), + migrations.AddField( + model_name='player', + name='diagnosis', + field=models.ForeignKey(help_text='Диагноз', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='player_diagnosis', to='main.diagnosis', verbose_name='Диагноз'), + ), + migrations.AddField( + model_name='player', + name='discipline_level', + field=models.ForeignKey(help_text='Числовой статус', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='player_disciplines_levels', to='main.disciplinelevel', verbose_name='Числовой статус'), + ), + migrations.AddField( + model_name='player', + name='discipline_name', + field=models.ForeignKey(help_text='Дисциплина', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='player_disciplines_names', to='main.disciplinename', verbose_name='Дисциплина'), + ), + migrations.AddField( + model_name='player', + name='team', + field=models.ManyToManyField(help_text='Команда', related_name='team_players', to='main.team', verbose_name='Команда'), + ), + migrations.AddField( + model_name='document', + name='player', + field=models.ForeignKey(blank=True, default=core.constants.MainConstantsStr['EMPTY_VALUE_DISPLAY'], help_text='Игрок', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='player_documemts', to='main.player', verbose_name='Игрок'), + ), + migrations.AddField( + model_name='disciplinelevel', + name='discipline_name', + field=models.ForeignKey(help_text='Дисциплина', on_delete=django.db.models.deletion.CASCADE, related_name='levels', to='main.disciplinename', verbose_name='Дисциплина'), + ), + migrations.AddField( + model_name='diagnosis', + name='nosology', + field=models.ForeignKey(help_text='Нозология', max_length=core.constants.MainConstantsInt['CLASS_FIELD_LENGTH'], on_delete=django.db.models.deletion.CASCADE, related_name='diagnosis', to='main.nosology', verbose_name='Нозология'), + ), + migrations.AddConstraint( + model_name='team', + constraint=models.UniqueConstraint(fields=('name', 'city', 'discipline_name'), name='team_city_unique'), + ), + migrations.AddConstraint( + model_name='staffteammember', + constraint=models.UniqueConstraint(fields=('staff_member', 'staff_position'), name='staff_member_position_unique'), + ), + migrations.AddConstraint( + model_name='player', + constraint=models.UniqueConstraint(fields=('name', 'surname', 'patronymic', 'birthday'), name='player_unique'), + ), + migrations.AddConstraint( + model_name='document', + constraint=models.UniqueConstraint(fields=('file', 'player'), name='player_docume_unique'), + ), + ] diff --git a/adaptive_hockey_federation/main/migrations/0002_setting_disciplines.py b/adaptive_hockey_federation/main/migrations/0002_setting_disciplines.py new file mode 100644 index 00000000..5a388d36 --- /dev/null +++ b/adaptive_hockey_federation/main/migrations/0002_setting_disciplines.py @@ -0,0 +1,37 @@ +from core.constants import DISCIPLINE_LEVELS +from django.db import migrations + + +def set_default_disciplines(apps, schema_editor): + DisciplineName = apps.get_model("main", "DisciplineName") + DisciplineName.objects.bulk_create( + [DisciplineName(name=name) for name in set(DISCIPLINE_LEVELS.keys())] + ) + + +def set_discipline_levels_to_discipline_name(apps, schema_editor): + DisciplineName = apps.get_model("main", "DisciplineName") + DisciplineLevel = apps.get_model("main", "DisciplineLevel") + for discipline_name in DisciplineName.objects.all(): + for discipline_level in DISCIPLINE_LEVELS[discipline_name.name]: + new_discipline_level = DisciplineLevel( + name=discipline_level, + discipline_name=discipline_name, + ) + new_discipline_level.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("main", "0001_initial"), + ] + + operations = [ + migrations.RunPython( + set_default_disciplines, + ), + migrations.RunPython( + set_discipline_levels_to_discipline_name, + ), + ] diff --git a/adaptive_hockey_federation/main/migrations/0003_gamedataplayer.py b/adaptive_hockey_federation/main/migrations/0003_gamedataplayer.py new file mode 100644 index 00000000..184cd33d --- /dev/null +++ b/adaptive_hockey_federation/main/migrations/0003_gamedataplayer.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.13 on 2024-06-27 18:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0002_setting_disciplines'), + ] + + operations = [ + migrations.CreateModel( + name='GameDataPlayer', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.JSONField(verbose_name='Данные игры')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('player', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='game_data_players', to='main.player')), + ], + options={ + 'verbose_name': 'Данные игрока', + 'verbose_name_plural': 'Данные игроков', + }, + ), + ] diff --git a/adaptive_hockey_federation/main/migrations/0004_delete_gamedataplayer.py b/adaptive_hockey_federation/main/migrations/0004_delete_gamedataplayer.py new file mode 100644 index 00000000..126900c8 --- /dev/null +++ b/adaptive_hockey_federation/main/migrations/0004_delete_gamedataplayer.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.13 on 2024-07-09 15:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0003_gamedataplayer'), + ] + + operations = [ + migrations.DeleteModel( + name='GameDataPlayer', + ), + ] diff --git a/adaptive_hockey_federation/main/migrations/__init__.py b/adaptive_hockey_federation/main/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/main/mixins.py b/adaptive_hockey_federation/main/mixins.py new file mode 100644 index 00000000..f660e5a9 --- /dev/null +++ b/adaptive_hockey_federation/main/mixins.py @@ -0,0 +1,24 @@ +from core.utils import generate_file_name, is_uploaded_file_valid +from main.models import Document + + +class FileUploadMixin: + """Класс-миксин для загрузки файлов.""" + + @staticmethod + def add_new_documents(player, new_files_names, new_files_paths): + """Метод для добавления документов.""" + for name, file in zip(new_files_names, new_files_paths, strict=False): + if is_uploaded_file_valid(file): + file.name = generate_file_name( + file.name, + str(player.id) + "-" + name, + ) + Document.objects.create(player=player, file=file, name=name) + + @staticmethod + def delete_documents(player, deleted_files_paths): + """Метод для удаления документов.""" + for doc in player.player_documemts.all(): + if doc.file.url in deleted_files_paths: + doc.delete() diff --git a/adaptive_hockey_federation/main/models.py b/adaptive_hockey_federation/main/models.py new file mode 100644 index 00000000..c3b8d93b --- /dev/null +++ b/adaptive_hockey_federation/main/models.py @@ -0,0 +1,480 @@ +from typing import TYPE_CHECKING + +from core.constants import ( + GENDER_CHOICES, + PLAYER_POSITION_CHOICES, + STAFF_POSITION_CHOICES, + MainConstantsInt, + MainConstantsStr, +) +from core.validators import fio_validator, validate_date_birth +from django.db import models +from django.db.models.signals import post_delete +from django.dispatch.dispatcher import receiver +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from phonenumber_field.modelfields import PhoneNumberField +from phonenumber_field.validators import validate_international_phonenumber +from users.models import User +from users.validators import zone_code_without_seven_hundred + + +if TYPE_CHECKING: + from django.db.models import QuerySet + + +class BaseUniqueName(models.Model): + """Базовый класс для других моделей с повторяющимся полем name.""" + + name = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Наименование"), + help_text=_("Наименование"), + unique=True, + ) + + class Meta: + ordering = ("name",) + abstract = True + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + @classmethod + def get_by_name(cls, name: str): + """Возвращает объект БД по наименованию (полю "name").""" + name = name.strip() + res: QuerySet = cls.objects.filter(name=name) # type: ignore + if res.exists(): + return res.first() + return None + + +class City(BaseUniqueName): + """Модель Город.""" + + class Meta: + verbose_name = "Город" + verbose_name_plural = "Города" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class DisciplineName(BaseUniqueName): + """Модель дисциплин (следж-хоккей, хоккей для незрячих, спец. хоккей).""" + + class Meta: + verbose_name = "Название дисциплины" + verbose_name_plural = "Названия дисциплин" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class DisciplineLevel(BaseUniqueName): + """Модель классификация, статусы дисциплин.""" + + name = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Наименование"), + help_text=_("Наименование"), + ) + + discipline_name = models.ForeignKey( + DisciplineName, + on_delete=models.CASCADE, + verbose_name=_("Дисциплина"), + help_text=_("Дисциплина"), + related_name="levels", + ) + + class Meta: + verbose_name = "Классификация/статус дисциплины" + verbose_name_plural = "Классификация/статусы дисциплин" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class Nosology(BaseUniqueName): + """Модель Нозология.""" + + class Meta: + verbose_name = "Нозология" + verbose_name_plural = "Нозология" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class Diagnosis(BaseUniqueName): + """Модель Диагноз.""" + + nosology = models.ForeignKey( + Nosology, + on_delete=models.CASCADE, + max_length=MainConstantsInt.CLASS_FIELD_LENGTH, + verbose_name=_("Нозология"), + help_text=_("Нозология"), + related_name="diagnosis", + ) + + class Meta: + verbose_name = "Диагноз" + verbose_name_plural = "Диагнозы" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + +class BasePerson(models.Model): + """Абстрактная модель с базовой персональной информацией.""" + + surname = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Фамилия"), + help_text=_("Фамилия"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + validators=[fio_validator()], + ) + name = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Имя"), + help_text=_("Имя"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + validators=[fio_validator()], + ) + patronymic = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + blank=True, + verbose_name=_("Отчество"), + help_text=_("Отчество"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + validators=[fio_validator()], + ) + + class Meta: + ordering = ("surname", "name", "patronymic") + abstract = True + + def __str__(self): + """Метод, использующий полное ФИО для строкового представления.""" + return " ".join([self.surname, self.name, self.patronymic]) + + +class StaffMember(BasePerson): + """Модель сотрудник.""" + + phone = PhoneNumberField( + blank=True, + validators=[ + validate_international_phonenumber, + zone_code_without_seven_hundred, + ], + verbose_name=_("Актуальный номер телефона"), + help_text=_("Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ"), + ) + + class Meta: + verbose_name = "Сотрудник" + verbose_name_plural = "Сотрудники" + constraints = [ + models.UniqueConstraint( + name="staff_member_unique", + fields=[ + "name", + "surname", + "patronymic", + ], + ), + ] + + def __str__(self): + """Метод, использующий полное ФИО для строкового представления.""" + return " ".join([self.surname, self.name, self.patronymic]) + + +class Team(BaseUniqueName): + """Модель команды.""" + + city = models.ForeignKey( + City, + on_delete=models.CASCADE, + verbose_name=_("Город откуда команда"), + help_text=_("Город откуда команда"), + ) + discipline_name = models.ForeignKey( + DisciplineName, + on_delete=models.CASCADE, + verbose_name=_("Дисциплина команды"), + help_text=_("Дисциплина команды"), + ) + curator = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + verbose_name=_("Куратор команды"), + help_text=_("Куратор команды"), + related_name="team", + ) + + class Meta: + default_related_name = "teams" + verbose_name = "Команда" + verbose_name_plural = "Команды" + constraints = [ + models.UniqueConstraint( + name="team_city_unique", + fields=["name", "city", "discipline_name"], + ), + ] + permissions = [ + ("list_view_team", "Can view list of Команда"), + ] + + def __str__(self): + """Метод, определяющий строковое представление объекта.""" + if self.city: + return f"{self.name} - {self.city}" + return self.name + + +class StaffTeamMember(models.Model): + """Модель сотрудник команды.""" + + staff_member = models.ForeignKey( + StaffMember, + on_delete=models.CASCADE, + verbose_name=_("Сотрудник"), + help_text=_("Сотрудник"), + ) + staff_position = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + choices=STAFF_POSITION_CHOICES, + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + verbose_name=_("Статус сотрудника"), + help_text=_("Статус сотрудника"), + ) + team = models.ManyToManyField( + Team, + related_name="team_members", + verbose_name=_("Команда"), + help_text=_("Команда"), + default="Свободный агент", + blank=True, + ) + qualification = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + blank=True, + verbose_name=_("Квалификация"), + help_text=_("Квалификация"), + ) + notes = models.TextField( + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + verbose_name=_("Описание"), + help_text=_("Описание"), + blank=True, + ) + + class Meta: + verbose_name = "Сотрудник команды" + verbose_name_plural = "Сотрудники команды" + constraints = [ + models.UniqueConstraint( + name="staff_member_position_unique", + fields=[ + "staff_member", + "staff_position", + ], + ), + ] + permissions = [ + ("list_view_staff", "Can view list of Персонала команды"), + ] + + def __str__(self): + """Метод, использующий полное ФИО для строкового представления.""" + return " ".join( + [ + self.staff_member.surname, + self.staff_member.name, + self.staff_member.patronymic, + ], + ) + + def get_name_and_staff_position(self): + """Метод для получения строки с данными сотрудника команды.""" + return f"{self.__str__()} ({self.staff_position})" + + +class Player(BasePerson): + """ + Модель игрока. + + Связь с командой "многие ко многим" на случай включения игрока + в сборную, помимо основного состава. + """ + + diagnosis = models.ForeignKey( + Diagnosis, + on_delete=models.SET_NULL, + null=True, + related_name="player_diagnosis", + verbose_name=_("Диагноз"), + help_text=_("Диагноз"), + ) + discipline_name = models.ForeignKey( + DisciplineName, + on_delete=models.SET_NULL, + null=True, + related_name="player_disciplines_names", + verbose_name=_("Дисциплина"), + help_text=_("Дисциплина"), + ) + discipline_level = models.ForeignKey( + DisciplineLevel, + on_delete=models.SET_NULL, + null=True, + related_name="player_disciplines_levels", + verbose_name=_("Числовой статус"), + help_text=_("Числовой статус"), + ) + team = models.ManyToManyField( + Team, + related_name="team_players", + verbose_name=_("Команда"), + help_text=_("Команда"), + ) + birthday = models.DateField( + verbose_name=_("Дата рождения"), + help_text=_("Дата рождения"), + validators=[validate_date_birth], + ) + addition_date = models.DateField( + verbose_name=_("Дата добавления"), + default=timezone.now, + help_text=_("Дата добавления в базу данных"), + ) + gender = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + choices=GENDER_CHOICES, + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + verbose_name=_("Пол"), + help_text=_("Пол"), + ) + level_revision = models.TextField( + verbose_name=_("Игровая классификация"), + help_text=_("Игровая классификация"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + blank=True, + ) + position = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + choices=PLAYER_POSITION_CHOICES, + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + verbose_name=_("Игровая позиция"), + help_text=_("Игровая позиция"), + ) + number = models.IntegerField( + default=MainConstantsInt.DEFAULT_VALUE, + verbose_name=_("Номер игрока"), + help_text=_("Номер игрока"), + ) + is_captain = models.BooleanField( + default=False, + verbose_name=_("Капитан"), + ) + is_assistent = models.BooleanField( + default=False, + verbose_name=_("Ассистент"), + ) + identity_document = models.TextField( + verbose_name=_("Удостоверение личности"), + help_text=_("Удостоверение личности"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + blank=True, + ) + + class Meta(BasePerson.Meta): + default_related_name = "players" + verbose_name = "Игрок" + verbose_name_plural = "Игроки" + # TODO Раскомментировать, когда будет ручное добавление игроков + # ограничение на дублирование записей + constraints = [ + models.UniqueConstraint( + name="player_unique", + fields=[ + "name", + "surname", + "patronymic", + "birthday", + # 'position', + # 'number' + ], + ), + ] + permissions = [ + ("list_view_player", "Can view list of Игрок"), + ] + + def __str__(self): + """Метод, использующий полное ФИО для строкового представления.""" + return " ".join([self.surname, self.name, self.patronymic]) + + def get_name_and_position(self): + """Метод для получения строки с данными игрока и его позицией.""" + return f"{self.__str__()} ({self.position})" + + +class Document(BaseUniqueName): + """Модель Документы для загрузки.""" + + name = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Наименование"), + help_text=_("Наименование"), + ) + file = models.FileField( + upload_to="players_documents", + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + unique=True, + ) + player = models.ForeignKey( + Player, + on_delete=models.CASCADE, + related_name="player_documemts", + verbose_name=_("Игрок"), + help_text=_("Игрок"), + default=MainConstantsStr.EMPTY_VALUE_DISPLAY, + blank=True, + null=True, + ) + + class Meta: + verbose_name = "Документ" + verbose_name_plural = "Документы" + constraints = [ + models.UniqueConstraint( + name="player_docume_unique", + fields=["file", "player"], + ), + ] + + def __str__(self): + """Метод, определяющий строковое представление объекта.""" + return f"Документ игрока: {self.player}" + + +@receiver(post_delete, sender=Document) +def document_file_delete(sender, instance, **kwargs): + if instance.file: + instance.file.delete(False) diff --git a/adaptive_hockey_federation/main/permissions.py b/adaptive_hockey_federation/main/permissions.py new file mode 100644 index 00000000..b4b1dc57 --- /dev/null +++ b/adaptive_hockey_federation/main/permissions.py @@ -0,0 +1,97 @@ +from django.contrib.auth.mixins import ( + PermissionRequiredMixin, + UserPassesTestMixin, +) +from django.shortcuts import get_object_or_404 +from main.models import Team + + +class CustomPermissionMixin(PermissionRequiredMixin, UserPassesTestMixin): + """ + Миксин, объединяющий функционал миксинов-родителей. + + PermissionRequiredMixin и UserPassesTestMixin имеют общего родителя + AccessMixin и оба переопределяют родительский метод dispatch(), в связи с + чем их одновременное прямое использование во вью-классе невозможно. + + Данный класс-миксин позволяет обойти это ограничение. + + Работает следующим образом: сначала проверяется наличие общих разрешений + (которые указываются в permission_required соответствующего представления). + Затем производится тест пользователя на какие-то конкретные условия, + которые определяются в методе test_func(). + + !!! Во вью-классе или миксине-наследнике необходимо переопределить метод + test_func() либо get_test_func(). + """ + + def dispatch(self, request, *args, **kwargs): + """Метод, обрабатывающий запрос и определяющий разрешение на доступ.""" + if not ( + PermissionRequiredMixin.has_permission(self) + and UserPassesTestMixin.get_test_func(self)() + ): + return self.handle_no_permission() + return super(PermissionRequiredMixin, self).dispatch( + request, + *args, + **kwargs, + ) + + +class PlayerIdPermissionsMixin(CustomPermissionMixin): + """ + Миксин настройки разрешений для вью-классов PlayerIdView. + + Ограничивает права представителя на доступ к представлениям игроков не + своих команд. + + Если пользователь не является представителем (т.е. role != AGENT), + разрешения будут определяться только общим permission_required, + определенным во вью-классе. + + Если пользователь является представителем, то проверяется условие, + что он имеет отношение к команде, к которой принадлежит игрок или в + которую добавляется новый игрок. + + При создании игрока, когда request не содержит ключа "team", считается, + что игрок добавляется в БД без связки с конкретной командой и + представителю будет отказано в доступе. + """ + + def test_func(self) -> bool | None: + """Метод определяет разрешение на доступ к представлениям игроков.""" + request = self.__getattribute__("request") + if not (user := request.user).is_agent: + return True + if self.__getattribute__("kwargs").get("pk", None): + player = self.__getattribute__("get_object")() + return player.team.filter(curator=user).exists() + if team_id := ( + request.GET.get("team", None) or request.POST.get("team", None) + ): + team = get_object_or_404(Team, id=team_id) + return team.curator == user + return False + + +class TeamEditPermissionsMixin(CustomPermissionMixin): + """ + Миксин настройки разрешений для вью-классов TeamIdView. + + Ограничивает права представителя на доступ к представлениям не своих + команд. + + В текущем виде подходит только для страниц просмотра или редактирования + основных данных команды, поскольку считается, что создание новой команды + не разрешается для представителя в принципе. + + Работает аналогично PlayerIdPermissionsMixin, кроме функционала создания + объекта. + """ + + def test_func(self) -> bool | None: + """Метод определяет разрешение на доступ к представлениям команд.""" + user = self.__getattribute__("request").user + team = self.__getattribute__("get_object")() + return not user.is_agent or team.curator == user diff --git a/adaptive_hockey_federation/main/schemas/__init__.py b/adaptive_hockey_federation/main/schemas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/main/schemas/main_schema.py b/adaptive_hockey_federation/main/schemas/main_schema.py new file mode 100644 index 00000000..c77488d8 --- /dev/null +++ b/adaptive_hockey_federation/main/schemas/main_schema.py @@ -0,0 +1,67 @@ +from django.urls import reverse + +no_search_pages = [ + "analytics", + "player_id", + "player_create", + "player_id_edit", + "user_create", + "user_update", + "team_create", + "team_update", + "teams_id", + "staff_create", + "staff_id", + "staff_id_edit", + "teams", + "competition_add", + "competition_update", + "competition_id", + "game_info", +] + +show_return_button = [ + "player_id", + "player_create", + "player_id_edit", + "user_create", + "user_update", + "team_create", + "team_update", + "teams_id", + "staff_create", + "staff_id", + "staff_id_edit", + "competition_add", + "competition_update", + "competition_id", + "game_create", + "staff_id_team_edit", + "staff_id_team_create", + "edit_team_players_numbers", + "game_edit", + "game_info", +] + + +def get_main_table_data(context): + table_data = [ + { + "surname": player.surname, + "name": player.name, + "birthday": player.birthday, + "gender": player.get_gender_display(), + "number": player.number, + "discipline_name": ( + player.discipline_name if player.discipline_name else None + ), + "discipline_level": ( + player.discipline_level if player.discipline_level else None + ), + "diagnosis": (player.diagnosis.name if player.diagnosis else None), + "url": reverse("main:player_id", args=[player.id]), + "id": player.pk, + } + for player in context["main"] + ] + return table_data diff --git a/adaptive_hockey_federation/main/schemas/player_schema.py b/adaptive_hockey_federation/main/schemas/player_schema.py new file mode 100644 index 00000000..b271b98f --- /dev/null +++ b/adaptive_hockey_federation/main/schemas/player_schema.py @@ -0,0 +1,70 @@ +from django.urls import reverse + +SEARCH_FIELDS: dict = { + "surname": "surname", + "name": "name", + "birthday": ("year", "month", "day"), + "gender": "gender", + "number": "number", + "discipline_name": "discipline_name__name", + "discipline_level": "discipline_level__name", + "team": "team__name", +} + + +def get_player_table_data(context): + table_data = [ + { + "surname": player.surname, + "name": player.name, + "birthday": player.birthday, + "gender": player.get_gender_display(), + "number": player.number, + "discipline_name": ( + player.discipline_name if player.discipline_name else None + ), + "discipline_level": ( + player.discipline_level if player.discipline_level else None + ), + "team": ([team.name for team in player.team.all()]), + "url": reverse("main:player_id", args=[player.id]), + "id": player.pk, + } + for player in context["players"] + ] + return table_data + + +def get_player_fields_personal(player): + data = [ + ("Фамилия", player.surname), + ("Имя", player.name), + ("Отчество", player.patronymic), + ("Пол", player.gender), + ("Дата рождения", player.birthday), + ("Удостоверение личности", player.identity_document), + ("Дисциплина", player.discipline_name), + ("Числовой статус", player.discipline_level), + ("Нозология", player.diagnosis.nosology), + ("Диагноз", player.diagnosis.name), + ] + return data + + +def get_player_fields(player): + player_teams = [ + { + "name": team.name, + "url": reverse("main:teams_id", args=[team.id]), + } + for team in player.team.all() + ] + player_fields = [ + ("Команда", player_teams), + ("Игровая классификация", player.level_revision), + ("Капитан", player.is_captain), + ("Ассистент", player.is_assistent), + ("Игровая позиция", player.position), + ("Номер игрока", player.number), + ] + return player_fields diff --git a/adaptive_hockey_federation/main/schemas/staff_schema.py b/adaptive_hockey_federation/main/schemas/staff_schema.py new file mode 100644 index 00000000..4bd7dd0c --- /dev/null +++ b/adaptive_hockey_federation/main/schemas/staff_schema.py @@ -0,0 +1,60 @@ +from django.urls import reverse + +STAFF_SEARCH_FIELDS = { + "surname": "surname", + "name": "name", + "patronymic": "patronymic", + "phone": "phone", +} + + +def get_staff_table_data(context): + table_data = [ + { + "surname": staff.surname, + "name": staff.name, + "patronymic": staff.patronymic, + "phone": staff.phone, + "url": reverse("main:staff_id", args=[staff.id]), + "id": staff.pk, + } + for staff in context["staffs"] + ] + return table_data + + +def get_staff_fields(staff): + staff_fields = [ + ("Фамилия", staff.surname), + ("Имя", staff.name), + ("Отчество", staff.patronymic), + ("Номер телефона", staff.phone), + ] + return staff_fields + + +def add_pisition_in_context(queryset=None): + """Функция добавления формы staff_member по позициям в context.""" + team_fields = [] + if queryset.exists(): + for staff_team in queryset: + team_fields.append( + ( + "Команда", + ", ".join( + ( + [team.name for team in staff_team.team.all()] + if staff_team.team.all().exists() + else ["Свободный агент"] + ), + ), + ), + ) + team_fields.append( + ("Квалификация", staff_team.qualification), + ) + team_fields.append( + ("Описание", staff_team.notes), + ) + return staff_team, team_fields + return None, None diff --git a/adaptive_hockey_federation/main/schemas/team_schema.py b/adaptive_hockey_federation/main/schemas/team_schema.py new file mode 100644 index 00000000..35fd30ee --- /dev/null +++ b/adaptive_hockey_federation/main/schemas/team_schema.py @@ -0,0 +1,118 @@ +from core.constants import STAFF_POSITION_CHOICES, StaffPosition +from django.urls import reverse +from main.controllers.utils import get_player_href + +TEAM_SEARCH_FIELDS = { + "name": "name", + "discipline_name": "discipline_name__name", + "city": "city__name", +} + +TEAM_TABLE_HEAD = { + "name": "Название", + "discipline_name": "Дисциплина", + "city": "Город", + "team_structure": "Состав команды", +} + + +def get_staff_position_slug(staff_position: str) -> str: + slugs = {StaffPosition.TRAINER: "coach", StaffPosition.OTHER: "pusher"} + if staff_position in slugs.keys(): + return slugs[staff_position] + return "no_position_found" + + +def get_staff_table(team): + staff_table = [ + { + "position": staff_position[1].title(), + "head": { + "number": "№", + "surname": "Фамилия", + "name": "Имя", + "position": "Квалификация", + "note": "Примечание", + }, + "data": [ + { + "number": i + 1, + "surname": staff.staff_member.surname, + "name": staff.staff_member.name, + "position": staff.qualification, + "staff_position_slug": get_staff_position_slug( + staff.staff_position, + ), + "note": staff.notes, + "id": staff.id, + } + for i, staff in enumerate( + team.team_members.filter(staff_position=staff_position[1]), + ) + ], + } + for staff_position in STAFF_POSITION_CHOICES + ] + return staff_table + + +def get_players_table(players): + players_table = { + "name": "Игроки", + "head": { + "full_name": "Фамилия, Имя", + "birthday": "Д.Р.", + "gender": "Пол", + "position": "Квалификация", + "diagnosis": "Диагноз", + "discipline_name": "Дисциплина", + "discipline_level": "Числовой статус", + "number": "Номер игрока", + "level_revision": "Игровая классификация", + }, + "data": [ + { + "full_name_link": get_player_href(player), + "birthday": player.birthday, + "gender": player.get_gender_display(), + "position": player.get_position_display(), + "diagnosis": ( + player.diagnosis.name if player.diagnosis else None + ), + "discipline_name": ( + player.discipline_name if player.discipline_name else None + ), + "discipline_level": ( + player.discipline_level + if player.discipline_level + else None + ), + "number": player.number, + "level_revision": player.level_revision, + "id": player.pk, + } + for player in players + ], + } + return players_table + + +def get_team_table_data(teams, user): + table_data = [] + for team in teams: + team_data = { + "id": team.id, + "name": team.name, + "discipline_name": team.discipline_name, + "city": team.city, + "_ref_": { + "name": "Посмотреть", + "type": "button", + "url": reverse("main:teams_id", args=[team.id]), + }, + "allow_edit": user.is_admin + or (user.is_agent and team.curator == user), + } + table_data.append(team_data) + + return table_data diff --git a/adaptive_hockey_federation/main/urls.py b/adaptive_hockey_federation/main/urls.py new file mode 100644 index 00000000..2b72ea5a --- /dev/null +++ b/adaptive_hockey_federation/main/urls.py @@ -0,0 +1,141 @@ +from django.urls import include, path +from main.controllers import ( + ajax, + main_views, + player_views, + staff_views, + team_views, +) + +app_name = "main" + +main_urlpatterns = [ + path("", main_views.MainView.as_view(), name="main"), +] + +players_urlpatterns = [ + path("", player_views.PlayersListView.as_view(), name="players"), + path( + "create/", + player_views.PlayerIDCreateView.as_view(), + name="player_create", + ), + path( + "/", + player_views.PlayerIdView.as_view(), + name="player_id", + ), + path( + "/edit/", + player_views.PlayerIDEditView.as_view(), + name="player_id_edit", + ), + path( + "/delete/", + player_views.PlayerIDDeleteView.as_view(), + name="player_id_delete", + ), + path( + "/games_video/", + player_views.PlayerGamesVideo.as_view(), + name="player_id_games_video", + ), + path( + "deleted/", + player_views.player_id_deleted, + name="player_id_deleted", + ), +] + +teams_urlpatterns = [ + path("", team_views.TeamListView.as_view(), name="teams"), + path( + "create/", + team_views.CreateTeamView.as_view(), + name="team_create", + ), + path( + "/", + team_views.TeamIdView.as_view(), + name="teams_id", + ), + path( + "/edit/", + team_views.UpdateTeamView.as_view(), + name="team_update", + ), + path( + "/delete/", + team_views.DeleteTeamView.as_view(), + name="team_delete", + ), + path( + "/fire_staff//", + team_views.FireStaffFromTeam.as_view(), + name="fire_staff_from_team", + ), +] + +staffs_urlpatterns = [ + path( + "", + staff_views.StaffMemberListView.as_view(), + name="staffs", + ), + path( + "create/", + staff_views.StaffMemberIdCreateView.as_view(), + name="staff_create", + ), + path( + "/", + staff_views.StaffMemberIdView.as_view(), + name="staff_id", + ), + path( + "/edit/", + staff_views.StaffMemberIdEditView.as_view(), + name="staff_id_edit", + ), + path( + "/delete/", + staff_views.StaffMemberIdDeleteView.as_view(), + name="staff_id_delete", + ), + path( + "//team/create/", + staff_views.StaffMemberIdTeamCreateView.as_view(), + name="staff_id_team_create", + ), + path( + "//team/edit/", + staff_views.StaffMemberIDTeamEditView.as_view(), + name="staff_id_team_edit", + ), + path( + "//team/delete/", + staff_views.StaffMemberIdTeamDeleteView.as_view(), + name="staff_id_team_delete", + ), +] + +ajax_urlpatterns = [ + path( + "load-discipline-levels/", + ajax.load_discipline_levels, + name="ajax_load_discipline_levels", + ), + path( + "filter-discipline-search/", + ajax.filter_discipline_search, + name="ajax_filter_discipline_search", + ), +] + +urlpatterns = [ + path("", include(main_urlpatterns)), + path("players/", include(players_urlpatterns)), + path("teams/", include(teams_urlpatterns)), + path("staffs/", include(staffs_urlpatterns)), + path("ajax/", include(ajax_urlpatterns)), +] diff --git a/adaptive_hockey_federation/main/views.py b/adaptive_hockey_federation/main/views.py new file mode 100644 index 00000000..112c60cb --- /dev/null +++ b/adaptive_hockey_federation/main/views.py @@ -0,0 +1,7 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import render + + +@login_required +def main(request): + return render(request, "main/home/main.html") diff --git a/adaptive_hockey_federation/manage.py b/adaptive_hockey_federation/manage.py new file mode 100644 index 00000000..2c2dc592 --- /dev/null +++ b/adaptive_hockey_federation/manage.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" + +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + "core.config.dev_settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?", + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/adaptive_hockey_federation/service/a_hockey_requests.py b/adaptive_hockey_federation/service/a_hockey_requests.py new file mode 100644 index 00000000..c48a5594 --- /dev/null +++ b/adaptive_hockey_federation/service/a_hockey_requests.py @@ -0,0 +1,25 @@ +import logging +from urllib.parse import urljoin + +import requests +from django.conf import settings +from requests.exceptions import RequestException + +logger = logging.getLogger(__name__) + + +def check_api_health_status() -> None: + """ + Отправка запроса на эндпоинт /health для проверки состояния сервиса. + + :raises RequestException: Если возникла ошибка. + """ + try: + response = requests.get( + urljoin(settings.PROCESSING_SERVICE_BASE_URL, "/health"), + ) + response.raise_for_status() + except RequestException as error: + raise RequestException( + f"Сервис по обработке видео недоступен: {error}", + ) from error diff --git a/adaptive_hockey_federation/service/mock_ds_server/celeryconfig.py b/adaptive_hockey_federation/service/mock_ds_server/celeryconfig.py new file mode 100644 index 00000000..3a665815 --- /dev/null +++ b/adaptive_hockey_federation/service/mock_ds_server/celeryconfig.py @@ -0,0 +1,10 @@ +broker_url = "redis://localhost:6379/1" +broker_transport_options = { + "visibility_timeout": 360, + "queue_order_strategy": "priority", +} +result_backend = broker_url +accept_content = ["application/json"] +task_serializer = "json" +result_serializer = "json" +broker_connection_retry_on_startup = True diff --git a/adaptive_hockey_federation/service/mock_ds_server/constants.py b/adaptive_hockey_federation/service/mock_ds_server/constants.py new file mode 100644 index 00000000..d1bc3fef --- /dev/null +++ b/adaptive_hockey_federation/service/mock_ds_server/constants.py @@ -0,0 +1,5 @@ +# DELAY = 5 * 60 * 60 # в секундах +DELAY = 10 # в секундах +FRAMES_QUANTITY = 10 +MIN_FRAMES = 0 +MAX_FRAMES = 100_000 diff --git a/adaptive_hockey_federation/service/mock_ds_server/main.py b/adaptive_hockey_federation/service/mock_ds_server/main.py new file mode 100644 index 00000000..2b6bb59c --- /dev/null +++ b/adaptive_hockey_federation/service/mock_ds_server/main.py @@ -0,0 +1,68 @@ +from fastapi import FastAPI +from fastapi.responses import JSONResponse, HTMLResponse +from pydantic import BaseModel + +from worker import mock_ds_process + +app = FastAPI() + + +class RequestData(BaseModel): + """Модель для проверки корректности запроса.""" + + game_id: int + game_link: str + token: str + player_ids: list[list[int]] + player_numbers: list[list[int]] + team_ids: list[int] + + +@app.get("/") +def main(): + """Имитация старта DS сервера.""" + page = "

Hockey Game Video Processing

" + return HTMLResponse(page) + + +@app.get("/status") +def status() -> JSONResponse: + """Проверка статуса DS сервера.""" + return JSONResponse(content={"status": "OK", "version": 1.0}) + + +@app.get("/version") +def version() -> JSONResponse: + """Запрос версии DS сервера.""" + return JSONResponse( + content={"version": 1.0}, + ) + + +@app.post("/process") +async def process(request_data: RequestData) -> JSONResponse: + """Имитация распознавания видео.""" + # TODO раскомментировать после добавления celery + # task = mock_ds_process.apply_async( + # kwargs={ + # "data": dict(request_data), + # }, + # ) + # response = task.get() + data = {"data": request_data.model_dump()} + response = mock_ds_process(**data) + return JSONResponse(content=response) + + +@app.post("/clean") +def clean() -> JSONResponse: + """Имитация очистки DS сервера.""" + return JSONResponse( + content={"Removed": "OK", "Objects": 0, "Size": "0 Mb"}, + ) + + +@app.get("/health") +def health() -> JSONResponse: + """Проверка статуса DS сервера.""" + return JSONResponse(content={"status": "OK", "version": 1.0}) diff --git a/adaptive_hockey_federation/service/mock_ds_server/worker.py b/adaptive_hockey_federation/service/mock_ds_server/worker.py new file mode 100644 index 00000000..f0734db9 --- /dev/null +++ b/adaptive_hockey_federation/service/mock_ds_server/worker.py @@ -0,0 +1,37 @@ +import logging +import random +from time import sleep + +from constants import DELAY, FRAMES_QUANTITY, MAX_FRAMES, MIN_FRAMES +from adaptive_hockey_federation.core.logging import configure_logging + +logger = logging.getLogger(__name__) + +configure_logging() + + +def mock_ds_process(*args, **kwargs) -> list[dict[str, int | list[int]]]: + logger.info("Старт заглушки DS сервера") + sleep(DELAY) + teams = zip( + kwargs["data"]["team_ids"], + kwargs["data"]["player_numbers"], + strict=True, + ) + response = [] + for team, numbers in teams: + for number in numbers: + frames = [ + random.randint(MIN_FRAMES, MAX_FRAMES) + for _ in range(FRAMES_QUANTITY) + ] + response.append( + { + "number": number, + "team": team, + "counter": 0, # непонятный параметр + "frames": sorted(frames), + }, + ) + logger.info("Завершение заглушки DS сервера") + return response diff --git a/adaptive_hockey_federation/service/video_processing.py b/adaptive_hockey_federation/service/video_processing.py new file mode 100644 index 00000000..f2fccef2 --- /dev/null +++ b/adaptive_hockey_federation/service/video_processing.py @@ -0,0 +1,30 @@ +import cv2 + + +# TODO моковая реализация для нарезки видео подключается к воркеру +# Пример сигнатуры +# input_file = os.path.join( +# os.path.dirname(__file__), +# "test_video/2023-09-26-11.mp4", +# ) +# output_file = os.path.join( +# os.path.dirname(__file__), +# "test_video/test.mp4", +# ) +# frames = [i for i in range(15000, 15430, 5)] + + +def slicing_video_with_player_frames(input_file, output_file, frames, fps=25): + cap = cv2.VideoCapture(input_file) + width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + fourcc = cv2.VideoWriter_fourcc(*"mp4v") + output = cv2.VideoWriter(output_file, fourcc, fps, (width, height)) + + for fr in range(len(frames)): # frames - список фреймов с игроком + cap.set(cv2.CAP_PROP_POS_FRAMES, frames[fr]) + for _ in range(frames[fr], frames[fr] + 5): + _, frame = cap.read() + output.write(frame) + output.release() + cap.release() diff --git a/adaptive_hockey_federation/staticfiles/css/base_style.css b/adaptive_hockey_federation/staticfiles/css/base_style.css new file mode 100644 index 00000000..50c4771d --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/css/base_style.css @@ -0,0 +1,1413 @@ +html, body { + background-color: #ECF1F5; + margin: 0; + padding: 0; +} + +*::-webkit-scrollbar { + background-color: transparent; + width: 8px; + height: 8px; +} + +*::-webkit-scrollbar-track { + background-color: transparent; +} + +*::-webkit-scrollbar-thumb { + background-color: #269FBD; + border-radius: 8px; +} + +section { + padding: 100px; +} + +li { + list-style-type: none; +} + +.header-login { + width: 100%; + display: grid; + grid-template-columns: repeat(12, 1fr); + justify-content: space-between; + align-items: center; +} + +.header { + max-width: 100vw; + justify-content: space-between; + align-items: center; +} + +.header-logo { + margin: 0 18px 0 73px; + position: relative; + max-width: 250px; + max-height: 250px; + grid-column: 1 / 2; + z-index: 3; +} + +@media screen and (min-width: 768px) and (max-width: 1280px) { + .header-logo { + margin: 0 18px 0 30px; + max-width: 120px; + max-height: 120px; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .header-logo { + margin: 0 8px 0 8px; + max-width: 70px; + max-height: 70px; + } +} + +.footer-class { + background: url(../img/footer.png); + max-width: 100vw; +} + +.pg-list-active { + background-color: rgb(2, 157, 183); + color: #340061; + font-size: 18px; +} + +.form_create { + width: 500px; + height: 650px; +} + +.pg-list-active:hover { + background-color: rgb(2, 157, 183); + color: #340061; + font-size: 18px; +} + +.pg-list { + background-color: rgb(2, 157, 183); + color: white; + font-size: 18px; +} + +.pg-list:hover { + background-color: #340061; + color: #fff; +} + +.drawler_menu, .drawler_menu:focus { + color: #ffffff; + background-color: rgb(2, 157, 183); + border-radius: 6px; +} + +.drawler_menu:hover { + color: #fff; + background-color: #340061; +} + +.drawler-menu-active { + background-color: #340061 ; + color: #fff; + border-radius: 6px; +} + +.drawler-menu-active:hover { + background-color: #340061 ; + color: #fff; +} + +.header-ellipse { + position: absolute; + top : 0; + left: -20%; + min-width: 40%; + height: 100%; + background-color: #ecf1f5; + border-radius: 9999px; + z-index: 2; +} + +@media screen and (min-width: 768px) and (max-width: 1280px) { + .header-ellipse { + left: -22%; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .header-ellipse { + left: -20%; + } +} + +.header-rect-container { + position: relative; + display: block; + min-height: 100%; + grid-column: 4 / -1; + grid-row: 1; +} + +@media screen and (min-width: 1441px) and (max-width: 1920px) { + .header-rect-container { + grid-column: 6 / -1; + } +} + +.header-rect { + position: relative; + max-width: 100%; + min-height: 345px; + z-index: 1; +} + +@media screen and (min-width: 768px) and (max-width: 1280px) { + .header-rect { + min-height: 250px; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .header-rect { + min-height: 100px; + max-height: 200px; + } +} + +.header-text { + position: relative; + margin: 0; + padding: 0; + color: #340061; + grid-column: 2 / span 4; + grid-row: 1; + text-decoration: none; + font-family: sans-serif; + font-size: 40px; + font-weight: 700; + font-weight: bold; + z-index: 5; +} + +@media screen and (min-width: 1441px) and (max-width: 1920px) { + .header-text { + font-size: 48px; + } +} + +@media screen and (min-width: 1281px) and (max-width: 1440px) { + .header-text { + grid-column: 2 / span 4; + display: flex; + justify-content: center; + } +} + +@media screen and (min-width: 768px) and (max-width: 1280px) { + .header-text { + font-size: 26px; + } +} + +@media screen and (min-width: 400px) and (max-width: 767px) { + .header-text { + font-size: 14px; + } +} + +@media screen and (min-width: 320px) and (max-width: 399px) { + .header-text { + font-size: 10px; + } +} + +.form_login { + margin: 20px auto 0 auto; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} + +.form-login-form { + max-width: 400px; + display: flex; + flex-direction: column; + align-items: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .form-login-form { + max-width: 280px; + } +} + +.form-input { + width: 100%; +} + +.form-layout { + margin: 8px 0 20px 0; + height: 80px; + width: 100%; + font-size: 32px; + line-height: 1.2; + background-color: #fff; + border: 1px solid #00000020; + border-radius: 5px; + outline: none; +} + +.form-layout:focus { + border: 3px solid #E73259; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .form-layout { + height: 50px; + } +} + +.form_btn { + background-color: #64C2D1; + color: #fff; + width: 100%; + height: 80px; + font-size: 22px; + text-align: center; + border-style: none; + border-radius: 5px; +} + +.form_btn:hover { + background-color: rgb(64, 159, 172); +} + +.enter_btn { + width: 100%; + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.btsss { + width: 150px; +} + +.btns-edit-del { + margin: 0; + padding: 0; + margin: 0; + padding: 0; + background-color: #fff; + border: none; + display: flex; + justify-content: center; + align-items: center; +} + +.btns-edit-del:disabled { + background-color: #b1b1c2; +} + +.forget-button { + margin: 15px 0 15px 0; + text-decoration: none; + color: #E73259; + position: relative; +} + +.forget-button:hover { + color: #851d33; +} + +.errors { + width: 100%; +} + +.documents-link-player { + text-decoration: none; + color: #269FBD; +} + +.documents-link-player:hover { + text-decoration: none; + color: #340061; +} + +.button_out { + display: flex; + justify-content: center; + align-items: center; + margin-left: 80px; + background-color: #E73259; + border-radius: 39px; + width: 240px; + height: 60px; + font-size: 24px; + border-color: #E73259; + color: #fff; + text-decoration: none; +} + +.button_out:hover { + opacity: 0.9; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .button_out { + margin: 0; + } +} + +.reset_card { + display: flex; + justify-content: center; + align-items: center; + border: none; + align-items: center; +} + +.update_card { + width: 100%; +} + +.update_btn { + margin-top: 20px; + background-color: #64C2D1; + color: #fff; + width: 240px; + height: 40px; + font-size: 22px; + border: #fff; + text-decoration: none; +} + +.reser_card_form { + position: inherit; + align-items: center; + text-align: center; + border: black; + background-color: #ECF1F5; + .reset_btn { + padding: 1rem 2rem; + border-radius: 5px; + border: none; + background-color: #64C2D1; + color: #fff; + font-size: 22px; + text-decoration: none; + } +} + +.forget-button-done { + position: absolute; + text-align: center; + left: 85px; + margin-top: 20px; + background-color: #64C2D1; + color: #fff; + width: 200px; + height: 40px; + font-size: 22px; + border: #fff; + text-decoration: none; +} + +.container__fluid { + margin: 0; + padding: 0; + transition: width 0.5s; + display: flex; + min-height: 100vh; +} + +.container__flu { + width: 0; + opacity: 0; +} + +.rotate { + transform: rotate(180deg); +} + +.done_btn { + background-color: #64C2D1; + color: #fff; + position: inherit; + border: #fff; +} + +.done_btn_confirm { + background-color: #64C2D1; + color: #fff; + position: relative; + border: #fff; + width: 200px; + height: 40px; + left: -50px; + margin-top: 20px; +} + +.aside-navbar { + margin: 0; + padding: 0; + position: fixed; + top: 0; + height: 100%; + left: -282px; + background-color: #340061; + transition: left 0.3s ease; + z-index: 5; +} + +.aside-navbar_opened { + left: 0; +} + +.navbar-options { + background-color: #64c2d1; +} + +.navbar-btns { + border-color: #fff; + color: #fff; + cursor: pointer; +} + +.navbar-btns:hover { + border-color: #340061; + background-color: #340061; + color: #fff +} + +.footer-options { + background-color: #64c2d1; + height: 90px; +} + +.footer { + padding: 0; + margin: 0; + min-height: 81px; + min-height: 81px; + width: 100%; + position: fixed; + bottom: 0; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .footer { + display: flex; + justify-content: center; + align-items: center; + } +} + +.sourch-options { + width: 30px; + background-color: #fff; + border-color: #64C2D1; + height: 40px; +} + +@media (min-width: 1104px) { + .table-responsive { + overflow-x: hidden; + } +} + +.delete-icon { + width: 30px; + height: 30px; +} + +.delete-icon:hover path { + fill: #E73259; +} + +.edit-icon { + width: 30px; + height: 30px; +} + +.edit-icon:hover path { + fill: #340061; +} + +.icons-block { + display: flex; + justify-content: center; + align-items: center; +} + +.logo-zone { + display: flex; + justify-content: space-between; + align-items: center; + gap: 20px; + min-width: 250px; +} + +.main { + margin: 0; + padding-bottom: 90px; + display: flex; + align-items: center; + flex: 1; + flex-direction: column; + overflow-y: hidden; +} + +@media screen and (min-width: 320px) and (max-width: 768px) { + .main { + padding-bottom: 130px; + } +} + +.page { + display: flex; + flex-direction: column; + flex: 1; + max-height: 100vh; +} + +.icon { + display: flex; + justify-content: center; + align-items: center; +} + +.center-title { + display: flex; + justify-content: center; + align-items: center; +} + +.row-table { + display: flex; +} + +th { + background-color: #FFFFFF; + border: none; + border-radius: 8px; + text-align: center; + padding: 5px; +} + +td { + background-color: #FFFFFF; + border: none; + border-radius: 8px; + text-align: center; + padding: 5px; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + th, td { + font-size: 10px; + } +} + +@media screen and (min-width: 768px) and (max-width: 860px) { + th, td { + font-size: 14px; + } +} + +.table-bordered { + width: 100%; +} + +.table-container { + margin: 15px 0; + padding: 0; + width: 100vw; + width: 100vw; + max-height: 95vh; + overflow: auto; + overflow: auto; + text-align: center; + display: flex; + flex-direction: column; +} + +.table-container::-webkit-scrollbar { + background-color: transparent; + width: 8px; + height: 8px; +} + +.table-container::-webkit-scrollbar-track { + background-color: transparent; +} + +.table-container::-webkit-scrollbar-thumb { + background-color: #269FBD; + border-radius: 8px; +} + +.table-table { + padding: 10px 5px; + border-collapse: separate; + border-spacing: 4px; + width: 100%; +} + +.navigate { + margin: 0; + padding: 0 1rem; + display: flex; + flex-direction: column; + justify-content: center; +} + +.navbar-container { + display: flex; + justify-content: space-around; + align-items: center; + height: 100%; + position: relative; +} + +.nav-button { + position: absolute; + right: -50px; + bottom: 1%; + margin: 0; + padding: 5px 15px; + border: none; + border-radius: 0 8px 8px 0 / 8px 8px 8px 8px; + background-color: #340061; + outline: none; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .nav-button { + bottom: 10%; + } +} + +.navbar-footer { + display: flex; + justify-content: space-around; + align-items: center; + gap: 20px; + padding: 10px 0; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .navbar-footer { + gap: 0; + flex-direction: column; + } +} + +.items__group { + display: flex; + justify-content: center; + gap: 20px; + justify-content: center; + gap: 20px; +} + +.navbar-header { + display: flex; + justify-content: space-around; + align-items: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .navbar-header { + display: grid; + grid-template-columns: repeat(8, 1fr); + grid-template-rows: max-content 1fr; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .nav-signout-button { + grid-column: 8; + justify-self: center; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .nav-user-info { + grid-column: 1 / 3; + justify-self: center; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .user-info { + font-size: 10px; + } +} + +.search__button { + border-radius: 0 10px 10px 0; +} + +.select-form { + border-radius: 10px 0 0 10px; +} + +.height { + margin: 10px; + padding: 0; + max-height: 75%; + display: flex; + align-items: center; + justify-content: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .height { + grid-column: 1 / -1; + grid-row: 2; + } +} + +.group-input { + display: flex; + justify-content: center; +} + +.cell__buttons { + display: flex; + gap: 20px; + gap: 20px; + justify-content: center; + align-items: center; +} + +.container-divs { + display: flex; + justify-content: center; + align-items: center; +} + +.divs-limit { + margin: 30px 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-width: 70%; + gap: 20px; + text-align: center; +} + +.profile-col { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.triangle { + padding: 8px 0; + transform: rotate(0deg); + transition: transform 0.3s ease; +} + +.full-container { + display: flex; + flex-direction: column; + width: 100%; +} + +.analitics-filter { + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .analitics-filter { + width: 100%; + flex-direction: column; + justify-content: space-between; + gap: 50px; + } +} + +.teams-filter { + display: flex; + flex-direction: column; + align-items: center; + white-space: nowrap; + align-items: center; + white-space: nowrap; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .teams-filter { + min-width: 68px; + } +} + +.filter-item__wrapper { + width: 80%; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 15px; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 15px; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .filter-item__wrapper { + width: 100%; + grid-template-columns: repeat(2, 1fr); + grid-template-columns: repeat(2, 1fr); + } +} + +.filter-three-item__wrapper { + width: 80%; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 15px; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 15px; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .filter-three-item__wrapper { + width: 100%; + grid-template-columns: repeat(2, 1fr); + grid-template-columns: repeat(2, 1fr); + } +} + +.analitics-text-item { + min-width: max-content; +} + +.analitics-bg { + color: #fff; + margin: 30px 0; + background-color: #269FBD; + border-radius: 8px; +} + +.analitics-dashbord-wrapper { + margin: 0 auto; + max-width: 90%; +} + +.analitics-dashbord-panel-item { + width: 100%; + border-bottom: 2px dashed white; +} + +.analitics-grid { + width: 100%; + display: grid; + grid-template-columns: repeat(4, 1fr); +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .analitics-grid { + grid-template-columns: repeat(3, 1fr); + } +} + + +.analitics-dashbord-number { + font-size: 40px; + font-weight: 700; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .analitics-dashbord-number { + font-size: 18px; + } +} + +.analitics-dashbord-paragraph { + font-size: 20px; + font-weight: 400; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .analitics-dashbord-paragraph { + font-size: 10px; + } +} + +.base-input { + margin: 0; + padding: 0; + min-width: 80%; + min-height: 100%; + border: none; + background-color: transparent; + border-bottom: 2px solid transparent; + display: flex; + text-align: center; + transition: border-bottom 0.5s ease; + color: #340061; + font-weight: 700; +} + +.errorlist { + transition: border-bottom 0.5s ease; + text-align: center; + display: flex; + flex-direction: column; +} + +.errorlist li { + color: #E73259; +} + +.base-input-errors { + margin: 0; + padding: 0; + min-width: 100%; + min-height: 100%; + border: none; + background-color: transparent; + border: 2px red solid; + border-radius: 8px; + color: #E73259; + display: flex; + text-align: center; + transition: border-bottom 0.5s ease; +} + +.base-input-errors:focus { + color: #920000; + outline: none; + background-color: #fff; + text-align: left; +} + +.base-input:focus { + outline: none; + background-color: #fff; + border-bottom: 2px solid rgba(38, 159, 189, 0.5); + text-align: left; +} + +.base-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.base-date { + color: #340061; + font-family: Arial; + font-weight: 700; +} + +.base-date-errors { + color: red; + font-family: Arial; + font-weight: 700; +} + +.base-select { + margin: 0; + padding: 2px; + min-width: 60%; + color: #340061; + border: 2px solid #269FBD; + border-radius: 8px; + font-weight: 700; + display: flex; +} + +.selector-list { + margin: 0px 10px 0 10px; + padding: 2px; + min-width: 80%; + min-height: 180px; + color: #340061; + border: 2px solid #269FBD; + border-radius: 8px; + font-weight: 700; +} + +.base-select-errors { + margin: 0; + padding: 2px; + min-width: 80%; + border-radius: 8px; + font-weight: 700; + border: 2px red solid; + display: flex; + color: #E73259; +} + +.base-textarea { + margin: 0; + padding: 2px; + min-width: 80%; + border: 2px solid #269FBD; + border-radius: 8px; +} + +.base-checkbox { + width: 25px; + height: 25px; +} + +.error-page { + margin: 0; + padding: 0; + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-image: url('../img/fone.png'); + background-size: contain; +} + +.error-page__info-wrapper { + margin-top: 5%; + padding: 0; + width: 60%; + height: 50%; + border: 3px solid #269FBD; + border-radius: 8px; + background-color: rgba(100, 194, 209, 0.8); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .error-page__info-wrapper { + width: 80%; + height: 40%; + } +} + +.error-page__logo { + margin: 0; + padding: 0; +} + +.error-page__heading { + margin-top: 5%; + padding: 0; + font-weight: 700; + font-size: 72px; + line-height: 53.2px; + color: #340061; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .error-page__heading { + font-size: 40px; + } +} + +.error-page__paragraph { + margin: 5% 0; + padding: 0; + color: #FFF; + font-weight: 400; + font-size: 26px; + line-height: 42.56px; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .error-page__paragraph { + font-size: 20px; + } +} + +.error-page__link { + margin: 3% 0; + padding: 15px 30px; + background-color: #E73259; + border-radius: 39px; + text-decoration: none; + font-weight: 700; + font-size: 16px; + line-height: 30px; + color: #fff; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .error-page__link { + padding: 8px 16px; + font-size: 12px; + } +} + +.arrow-before { + padding-right: 26px; + background-image: url('../img/arrow.svg'); + background-repeat: no-repeat; + background-position: right 10px center; + background-size: 16px 8px; +} + +.analitics-button { + background-color: #269FBD; + color: #fff; +} + +.filter-button { + background-color: #269FBD; + color: #fff; +} + +.base-select-teams { + margin: 0px 10px 0 10px; + padding: 2px; + min-width: 80%; + min-height: 180px; + color: #340061; + border: 2px solid #269FBD; + border-radius: 8px; + font-weight: 700; + text-align: center; +} + +.base-select-teams-errors { + margin: 0px 10px 0 10px; + padding: 2px; + min-width: 80%; + min-height: 180px; + border: 2px solid #269FBD; + border-radius: 8px; + font-weight: 700; + text-align: center; + border: 2px red solid; + color: #E73259; +} + +.base-select-teams option:checked { + color: #340061; + background-color: white; +} + +.base-select-teams option:active { + box-shadow: 0 0 10px 100px #269FBD inset; + background-color: #269FBD; +} + +.base-select-teams option:hover{ + box-shadow: 0 0 10px 100px #269FBD inset; + transition: all .2s ease-in-out; +} + +.selectors-teams { + display: flex; + float: center; + min-width: 80%; + justify-content: center; +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .selectors-teams { + flex-direction: column; + } +} + +@media screen and (min-width: 320px) and (max-width: 767px) { + .selectors-teams { + flex-direction: column; + } +} + +.selectors-teams .help-icon { + background: url(../img/icon-unknown.svg) 0 0 no-repeat; + display: inline-block; + vertical-align: middle; + margin: -2px 0 0 2px; + width: 13px; + height: 13px; +} + +.table-table .selectors-teams h2 { + margin: 0 15px 5px 15px; + padding: 8px; + font-weight: 400; + font-size: 0.8125rem; + text-align: center; + background:#269FBD; + color: #fff; + border-radius: 10px; +} + +.selector-available { + width: 380px; + text-align: center; + margin-bottom: 5px; + display: flex; + flex-direction: column; +} + +.selectors-teams .selector-filter { + border: 1px solid ; + border-width: 0; + padding: 8px; + color: #666; + font-size: 0.625rem; + margin: 0; + text-align: left; +} + +.selectors-teams .selector-filter label { + float: left; + margin: 3px 0 0; + width: 18px; + height: 18px; + padding: 0; + overflow: hidden; + line-height: 1; + min-width: auto; +} + +.selectors-teams .selector-available input, +.selectors-teams .selector-chosen input { + width: 320px; + margin-left: 8px; + border-color: #269FBD; + border-radius: 8px; +} + +.help-tooltip { + cursor: help; +} + +.selectors-teams .search-label-icon { + background: url(../img/search.svg) 0 0 no-repeat; + display: inline-flex; + height: 1.125rem; + width: 1.125rem; +} + +.selectors-teams .selector-chosen:not(.selector-chosen--with-filtered) .list-footer-display { + display: none; +} +.selector-chosen { + width: 380px; + text-align: center; + margin-bottom: 5px; + display: flex; + flex-direction: column; +} + +.selector-chosen li { + margin: 0; + padding: 3px; + list-style-type: none; +} + +.selector-chosen .list-footer-display { + border: 1px solid #ccc; + border-top: none; + border-radius: 0 0 4px 4px; + margin: 0 0 10px; + padding: 8px; + text-align: center; + background: #79aec8; + color: #fff; + cursor: pointer; +} + +.selectors-teams .selector-chosen:not(.selector-chosen--with-filtered) .list-footer-display { + display: none; +} + +.selector-chosen .list-footer-display__clear { + color: #269FBD; +} + + +.table-table a { + text-decoration: none; + color: #269FBD; +} + +.table-table a:hover { + text-decoration: none; + color: #340061; +} + +.selector-left { + min-width: 95%; + text-align: center; + margin-bottom: 5px; + display: flex; + flex-direction: column; +} +.selector-left option:checked { + color: white; + background-color: #269FBD; +} + +.full-container .help-icon { + background: url(../img/icon-unknown.svg) 0 0 no-repeat; + display: inline-block; + vertical-align: middle; + margin: -2px 0 0 2px; + width: 13px; + height: 13px; +} + +.table-table .selectors-teams h2 { + margin: 0 15px 5px 15px; + padding: 8px; + font-weight: 400; + font-size: 0.8125rem; + text-align: center; + background:#269FBD; + color: #fff; + border-radius: 10px; + margin: 0 15px 5px 15px; + padding: 8px; + font-weight: 400; + font-size: 0.8125rem; + text-align: center; + background:#269FBD; + color: #fff; + border-radius: 10px; +} + +.search-form-control { + margin: 0; + padding: 0; + max-width: 80%; +} + +/* .picker { + display: flex; + justify-content: center; + gap: 2px; +} */ + +.picker__select { + min-width: max-content; + border-radius: 0; +} + +/* .picker__select:focus { + outline: none; +} */ diff --git a/adaptive_hockey_federation/staticfiles/css/bootstrap.min.css b/adaptive_hockey_federation/staticfiles/css/bootstrap.min.css new file mode 100644 index 00000000..691810b6 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.0 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#adb5bd;--bs-body-color-rgb:173,181,189;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(173, 181, 189, 0.75);--bs-secondary-color-rgb:173,181,189;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(173, 181, 189, 0.5);--bs-tertiary-color-rgb:173,181,189;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-body-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-body-color);--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:var(--bs-body-color);--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:var(--bs-body-color);--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#bacbe6;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#cbccce;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#bcd0c7;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#badce3;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#e6dbb9;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#dfc2c4;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#dfe0e1;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#373b3e;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-tertiary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-tertiary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>:disabled~label{color:#6c757d}.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-focus-border-color:#86b7fe;--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(13,110,253,var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(108,117,125,var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(25,135,84,var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(13,202,240,var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(255,193,7,var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(220,53,69,var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(248,249,250,var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(33,37,41,var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/adaptive_hockey_federation/staticfiles/css/img/forma.jpg b/adaptive_hockey_federation/staticfiles/css/img/forma.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f9d5a9222e101c6af22c9bbc0fe902b2276a9cc3 GIT binary patch literal 273858 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

yFR=`@TYP&9b6)V3THE#j^!oAj; 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 |AxWkx}s$e5BWtZ(Zabs(pxV1IUoxGQY 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�R{Cum@u~r@*!WX}z~ztPO4PwEwj%Y5 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�LN13F_ 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 literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/css/select.css b/adaptive_hockey_federation/staticfiles/css/select.css new file mode 100644 index 00000000..e6bfb370 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/css/select.css @@ -0,0 +1,589 @@ +.select2-container { + box-sizing: border-box; + width: 500px; + display: inline-block; + margin: 0; + position: relative; + vertical-align: middle; + color: #340061; + text-align: left; +} + +.select2-container .select2-selection--single { + box-sizing: border-box; + cursor: pointer; + display: block; + height: 30px; + user-select: none; + -webkit-user-select: none; + border: 2px solid #269FBD; + border-radius: 8px; +} + +.select2-container .select2-selection--single .select2-selection__rendered { + display: block; + padding-left: 8px; + padding-right: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.select2-container .select2-selection--single .select2-selection__clear { + background-color: transparent; + border: none; + font-size: 1em +} +.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { + padding-right: 8px; + padding-left: 20px +} +.select2-container .select2-selection--multiple { + box-sizing: border-box; + cursor: pointer; + display: block; + min-height: 32px; + user-select: none; + -webkit-user-select: none +} +.select2-container .select2-selection--multiple .select2-selection__rendered { + display: inline; + list-style: none; + padding: 0 +} +.select2-container .select2-selection--multiple .select2-selection__clear { + background-color: transparent; + border: none; + font-size: 1em +} +.select2-container .select2-search--inline .select2-search__field { + box-sizing: border-box; + border: none; + font-size: 100%; + margin-top: 5px; + margin-left: 5px; + padding: 0; + max-width: 100%; + resize: none; + height: 18px; + vertical-align: bottom; + font-family: sans-serif; + overflow: hidden; + word-break: keep-all +} +.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none +} +.select2-dropdown { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + box-sizing: border-box; + display: block; + position: absolute; + left: -100000px; + width: 100%; + z-index: 1051 +} +.select2-results { + display: block +} +.select2-results__options { + list-style: none; + margin: 0; + padding: 0 +} +.select2-results__option { + padding: 6px; + user-select: none; + -webkit-user-select: none +} +.select2-results__option--selectable { + cursor: pointer +} +.select2-container--open .select2-dropdown { + left: 0 +} +.select2-container--open .select2-dropdown--above { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} +.select2-container--open .select2-dropdown--below { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0 +} +.select2-search--dropdown { + display: block; + padding: 4px +} +.select2-search--dropdown .select2-search__field { + padding: 4px; + width: 100%; + box-sizing: border-box +} +.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none +} +.select2-search--dropdown.select2-search--hide { + display: none +} +.select2-close-mask { + border: 0; + margin: 0; + padding: 0; + display: block; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 99; + background-color: #fff; + filter: alpha(opacity=0) +} +.select2-hidden-accessible { + border: 0 !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(50%) !important; + clip-path: inset(50%) !important; + height: 1px !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + width: 1px !important; + white-space: nowrap !important +} + +.select2-container--default .select2-selection--single .select2-selection__rendered { + color: #340061; + border-radius: 8px; + font-weight: 700; + line-height: 28px; +} + +.select2-container--default .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + height: 26px; + margin-right: 20px; + padding-right: 0px +} +.select2-container--default .select2-selection--single .select2-selection__placeholder { + color: #999 +} +.select2-container--default .select2-selection--single .select2-selection__arrow { + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px +} +.select2-container--default .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0 +} +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left +} +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow { + left: 1px; + right: auto +} +.select2-container--default.select2-container--disabled .select2-selection--single { + background-color: #eee; + cursor: default +} +.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear { + display: none +} +.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px +} +.select2-container--default .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; + padding-bottom: 5px; + padding-right: 5px; + position: relative +} +.select2-container--default .select2-selection--multiple.select2-selection--clearable { + padding-right: 25px +} +.select2-container--default .select2-selection--multiple .select2-selection__clear { + cursor: pointer; + font-weight: bold; + height: 20px; + margin-right: 10px; + margin-top: 5px; + position: absolute; + right: 0; + padding: 1px +} +.select2-container--default .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + box-sizing: border-box; + display: inline-block; + margin-left: 5px; + margin-top: 5px; + padding: 0; + padding-left: 20px; + position: relative; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; + white-space: nowrap +} +.select2-container--default .select2-selection--multiple .select2-selection__choice__display { + cursor: default; + padding-left: 2px; + padding-right: 5px +} +.select2-container--default .select2-selection--multiple .select2-selection__choice__remove { + background-color: transparent; + border: none; + border-right: 1px solid #aaa; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + color: #999; + cursor: pointer; + font-size: 1em; + font-weight: bold; + padding: 0 4px; + position: absolute; + left: 0; + top: 0 +} +.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover, .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:focus { + background-color: #f1f1f1; + color: #333; + outline: none +} +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto +} +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__display { + padding-left: 5px; + padding-right: 2px +} +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + border-left: 1px solid #aaa; + border-right: none; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px +} +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__clear { + float: left; + margin-left: 10px; + margin-right: auto +} +.select2-container--default.select2-container--focus .select2-selection--multiple { + border: solid black 1px; + outline: 0 +} +.select2-container--default.select2-container--disabled .select2-selection--multiple { + background-color: #eee; + cursor: default +} +.select2-container--default.select2-container--disabled .select2-selection__choice__remove { + display: none +} +.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple { + border-top-left-radius: 0; + border-top-right-radius: 0 +} +.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} +.select2-container--default .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa +} +.select2-container--default .select2-search--inline .select2-search__field::-webkit-search-cancel-button { + background: transparent; + border: none; + outline: 0; + box-shadow: none; + -webkit-appearance: textfield +} +.select2-container--default .select2-results>.select2-results__options { + max-height: 200px; + overflow-y: auto +} +.select2-container--default .select2-results__option .select2-results__option { + padding-left: 1em +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__group { + padding-left: 0 +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__option { + margin-left: -1em; + padding-left: 2em +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -2em; + padding-left: 3em +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -3em; + padding-left: 4em +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -4em; + padding-left: 5em +} +.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -5em; + padding-left: 6em +} +.select2-container--default .select2-results__option--group { + padding: 0 +} +.select2-container--default .select2-results__option--disabled { + color: #999 +} +.select2-container--default .select2-results__option--selected { + background-color: #269FBD; + color: white; +} +.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable { + background-color: #269FBD; + color: white +} +.select2-container--default .select2-results__group { + cursor: default; + display: block; + padding: 6px +} +.select2-container--classic .select2-selection--single { + background-color: #f7f7f7; + border: 1px solid #aaa; + border-radius: 4px; + outline: 0; + background-image: -webkit-linear-gradient(top, #fff 50%, #eee 100%); + background-image: -o-linear-gradient(top, #fff 50%, #eee 100%); + background-image: linear-gradient(to bottom, #fff 50%, #eee 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0) +} +.select2-container--classic .select2-selection--single:focus { + border: 1px solid #5897fb +} +.select2-container--classic .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 28px +} +.select2-container--classic .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + height: 26px; + margin-right: 20px +} +.select2-container--classic .select2-selection--single .select2-selection__placeholder { + color: #999 +} +.select2-container--classic .select2-selection--single .select2-selection__arrow { + background-color: #ddd; + border: none; + border-left: 1px solid #aaa; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; + background-image: -webkit-linear-gradient(top, #eee 50%, #ccc 100%); + background-image: -o-linear-gradient(top, #eee 50%, #ccc 100%); + background-image: linear-gradient(to bottom, #eee 50%, #ccc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0) +} +.select2-container--classic .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0 +} +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left +} +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { + border: none; + border-right: 1px solid #aaa; + border-radius: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + left: 1px; + right: auto +} +.select2-container--classic.select2-container--open .select2-selection--single { + border: 1px solid #5897fb +} +.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { + background: transparent; + border: none +} +.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px +} +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; + background-image: -webkit-linear-gradient(top, #fff 0%, #eee 50%); + background-image: -o-linear-gradient(top, #fff 0%, #eee 50%); + background-image: linear-gradient(to bottom, #fff 0%, #eee 50%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0) +} +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + background-image: -webkit-linear-gradient(top, #eee 50%, #fff 100%); + background-image: -o-linear-gradient(top, #eee 50%, #fff 100%); + background-image: linear-gradient(to bottom, #eee 50%, #fff 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0) +} +.select2-container--classic .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; + outline: 0; + padding-bottom: 5px; + padding-right: 5px +} +.select2-container--classic .select2-selection--multiple:focus { + border: 1px solid #5897fb +} +.select2-container--classic .select2-selection--multiple .select2-selection__clear { + display: none +} +.select2-container--classic .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + display: inline-block; + margin-left: 5px; + margin-top: 5px; + padding: 0 +} +.select2-container--classic .select2-selection--multiple .select2-selection__choice__display { + cursor: default; + padding-left: 2px; + padding-right: 5px +} +.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { + background-color: transparent; + border: none; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + color: #888; + cursor: pointer; + font-size: 1em; + font-weight: bold; + padding: 0 4px +} +.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #555; + outline: none +} +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto +} +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__display { + padding-left: 5px; + padding-right: 2px +} +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px +} +.select2-container--classic.select2-container--open .select2-selection--multiple { + border: 1px solid #5897fb +} +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0 +} +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} +.select2-container--classic .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa; + outline: 0 +} +.select2-container--classic .select2-search--inline .select2-search__field { + outline: 0; + box-shadow: none +} +.select2-container--classic .select2-dropdown { + background-color: #fff; + border: 1px solid transparent +} +.select2-container--classic .select2-dropdown--above { + border-bottom: none +} +.select2-container--classic .select2-dropdown--below { + border-top: none +} +.select2-container--classic .select2-results>.select2-results__options { + max-height: 200px; + overflow-y: auto +} +.select2-container--classic .select2-results__option--group { + padding: 0 +} +.select2-container--classic .select2-results__option--disabled { + color: grey +} +.select2-container--classic .select2-results__option--highlighted.select2-results__option--selectable { + background-color: #3875d7; + color: #fff +} +.select2-container--classic .select2-results__group { + cursor: default; + display: block; + padding: 6px +} +.select2-container--classic.select2-container--open .select2-dropdown { + border-color: #5897fb +} diff --git a/adaptive_hockey_federation/staticfiles/favicon.ico b/adaptive_hockey_federation/staticfiles/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..572ff408b01fba42e0433a3ab52b9a1be1984ee4 GIT binary patch literal 15406 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 L|Ml$u?-lr8ad4DM literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/img/arrow.svg b/adaptive_hockey_federation/staticfiles/img/arrow.svg new file mode 100644 index 00000000..890dd2ea --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/img/arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/staticfiles/img/delete.png b/adaptive_hockey_federation/staticfiles/img/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..8f47b2f25395bcc3ab6cc17ada702de12fb085ef GIT binary patch literal 750 zcmVjbgfQHsD@c(}fd?I2n24ZNmWrac1-CNG4B@S7X|9g2NEGhe1v!Xp<}Bkxg8Yg;VbfoGy|tZ)7u_qWYqYP1}R3cqdqAua8 zv;qn?m~CO550NgKOMi;E+Fnq2F2Whwi{^>qYWtQ`yOcpacO*V8L)EHFB%V$Kw==-z z^(Vu6)w^QEyf5iGzryBz?w~n)bJXt&t-7#ESk^4R-qMx=)6`6| zC`~wkeH}^eCD&p64F{O$om%%Khg-riiV|)G$7nD4MWJ&GIE6k)UM1%_d@P(pyYTrV zhmV0%d&#5sTS2Z;YALO&&UF0 zt+44hiL#mr(Di?437!(C9A(X-@oBW+p=4=|rr?{xTr-r_RxV_UviJbuyV0^RhuAX=QPv+c>Wl>-%^+p~ O0000N#yCHEMhv}xCIjq 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 F{{y!ys}TSI literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/img/fone.png b/adaptive_hockey_federation/staticfiles/img/fone.png new file mode 100644 index 0000000000000000000000000000000000000000..8fcfd7b7b12a52b1b91e1c250b528572e12ab6ab GIT binary patch literal 255335 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

5tPLsRd97H#Pk+oQQpU z_AYqfDs<&x3C1t2E7r0V54;;9D)8KIBt;O!9c9s*LNU%rzw%{UXGnp_N5Nh(+k9s+ z7d_n%8f5==Pa^N^WFexVR4F-KA`E?0{3pe@J4NDq(|QaIdGEyDyKw$|F#HW_oq}!% z>8j0XfBtdt&oI~!P65VZVsF}i^i(U*I|mjjplEa5xW@EyFhxO^=}mIp zZ8cEkobcSH!VAv%WZoL$#gIrM&Hc&eB>{r`X_W6) z6l4HLtD?GH4v|A;&+0PJlM90U=jM%tdEbyR5ji71Xcg<3kmpC(V|OVZAOoim{Nhb5STHAE_ z5`|m{3gn1mR&+A@;7pU<TxG)?WH};NzrC$Y!h_k?`e~e{Jwp){YkNI9MB>w`v}$T>*+V)V5<)h5w3>PGY@=OS90N#el39P zmS{13noy&+C9QD`ikY8_O7-_;dMJjjq;(2P)%0&%|Ma=yOx++-Y&UM+45Uaj3n6M` z(4T9m9N*aU6l`{2T<2oWu06sofYdpX8SUickyZevT$xFpT*v}mSFZR$&gcuT5VL+uT&_aXGxh%O-~2piEM)c zik#o+Gpm|>)-NIw|MmCZYVWtc4~05bM(M2zB`e*5@CZ-6b8U2~>Sc^(qRbtAFl(UO z0#ep$rrzZ!gOiN7z=i%(Sn1ygh)$n69cTK^y*tSeZw=xb?Vo zc0l#^EPEp@hTCLkvWLsHBKDm@=dqZ)<$=2)UY*>RMdS$?umTnoN$lUhzv~{di2O*& z_{HPbuI6F!))aKCOUlvr+P!$DMu46^c`9=1Djv02tQ%UZrDeE0UkL+z{@nR6xU&hO zbPgI-05>C?WpybrBVD$A>Uo@)*$TqV~Oemi}CDz0c|;g6FwkN$FqpQh9ovb$~EnnX5+Y>=$l*U#j>y zELn7yNPSf$7$?&n^Sf%{-j1;ywR30AhC^@W{g$nbQKr5!=pHr}AVdKfqTn4Xl$d`x z9~6KV!FWJbj+d`ouI)=keNS50Zshoig12I-lL(1d<^f|Zvd9d&q9U(Dp9&U5yXZ9r zj!*{0%->Q zE@J|XW3=fhBtkgq)ff=Q>qXLC+Whve@q$hc4kJe2K_<{dPi-jL9lcZR*FdVe3UQ9!pbIV9k^;gE`< z^n*B0TDqbTF`YF^dS}Y5Gg!^YYz%369;m@gox)lRqKK98Aq7YJu5frn{8DbBak)-s z-x_Me{P1C_Z>9sTR_Nc0moG&`$~b4q@G(6N9#@aCt&AEWNm4fJQv{B$9U_^9-t)Z< z4f`fDYc?a+SHW?w`PHpMHNE0lOUHoT$Li2x$j!aG_hOy=4H73%JL3$7x3-9G-k0NJ z$+m69o|4KCu)1dy%sNi+KrDxz^;LwbuJK-;6!WdYt&sF&A*8O>RjgXdJAOu5j_1I8G)P|Z?SCC5FdubCh< zDmcb7V&QiTyLKr729%Hi!*%bhtEk6GaX0<^XrEVMFDq$bID7OJgAHK>`;z>O=ga&s zlA+8TNUT#M+Eh7m&YyK_r7c!q=Q)h`xL~&Gtl?@oW>%>ygr;A_nAeljMp&m-UB+Z91`7kIdl+1b!IspX+?1g;dpg_Ui>rW zWLNVjyu)*6&qXAB{hz<1a7gjM_;~xKcIg+i;8PLFJgePE50ca2z;7=HSa?XWsYhzD z(Y;=^yPjT$0uvEMA&PIS42#HyvO*C>y?XsBT8B8PCI*6xMV(uh$kmDBje|A$i_#a# zJ}Y6^K@n)#^Or9oV94DxTC5@+k$yjx^1<#?)wQ-h21UzhjQUMbs(`{8Nm;mmP&DKA zp=8t>Xc+++PY2=2{&M_nmaGe{TY+5Ouf0z2`RNorHp5Y{5<(0|483}Q(oS#;?K>E7MJ=(pJ zf)PE4+Cf?(^~HKgEu4G}FBv6ZC&XufDGwbi#u4TshR5k3PnYcmy!T?jz>Yu?GEM?F!u*D4S(Xl|B7`?Hv!D!)P zm~Y*>8QkF%>b`{XEoLaRUx~Ji(~KN>Qsht#y~WrRwff1Cf?GxFD&MEGQ@;j8Fd{ds zYQ4VlT&1~G%Yz3GquQHOsH1>xw1?VVtX!bApL))+34t+%@?+Znik;oJ!e{G4-wQUY zMp%#sK7X2mK=*)hmcz#u*h{MXIl*%BzSL?6Ox0pQh3oU|%Sm5zxtq4d8zA~r4>54DQ$Xb2suFco7^wy znm8oZb1n7HiV!iK71gqPzWLtZRz;+Wev?sG;G=qe=gx`PM|mn$y)fn+XHei+QfAt> zPMtc{adyGgihKP#=@tChdGJssGh&$Z_~&|^JdQLAXcgqnpfhrY7=cwOV(;PaAakJ6 zYM->%isTSYd4Kz~=wGgdvrlhKr1kDrac{FiZ~T^wG#>I($#~U|gU-F!$Y9(%w1yKT zGCj0{_Q1s?Ge`yn@zjUtTIi=5Bu=n4W`%O@-Cj@}s5SXs8L=y#7SLHE$d}h6Ow*Dr zviCPtRGO&0l0#Rk(TY*;TZ0bjx|D7R?2xNe9gL+EhI|9h`)tbf^ir|dgdUv3Z^J8= zPzBF}lR}Vd?37p|ju2?ay2Ysl6ap2g$784E&WW>E%bI`HpKb*zg zn%J{CGoC$t778=wy(gy-D%MCSSX}U9K^MHc?;R=kc=PtHVB3?ja~?!a@7}&k*SuT)i4bg%eaoAL1TU6iHA2{NqnC&;)eM$jNo<`L}kfIg9iblwj(^ zR2WATvHe0k<$1}W>kqd+zT7L6h8^NC+6&cl5qq%0bVq+Fs^biZ_^Woy`BOlscA?p+ zmwji@ivjk}0%8ZDQ`RT}mxBKYI3^r`6#eoj`$e7S`*~hc!t(Oeb8&O7rsvM$0E6(aCVX#DF)^IJM9R(58Y@iS#=9@QfM$Q|` zP=8h+K2ScbYtTWgbK{%FqrQM+PHEmwP%8sxc6a_g21zwGJ_ovy`?RZ)X+cR8%T_*{ zqfq2mch&ku6lGZ}P%++(ot21@vA0HAjk6Rw?|BplVn+Kep_CJ=FznW!v;Owo+uAQb z{oEIO5<;aA6c>uSV$9WSL3%DX;wZ1Hej;UG~g#G&4uhGjdl~HwAf|n)f zwhL)}o2gIe8++kCcs<E9Qc8DAX1xvQc5OQCgP`*+`Yi%8)^{g!Jq9jF0C`uQdh*;LY zs}aC|0ae?WoM8MD;81^YBtgrRG4d<iln-HvVn`pfG$qdI2#X^4#bKk>tf(8p3U?jfqZgTizsh)}9h=)B#vA1_=82gsUT z|NHM)gH|QlmlG0%$&mi+mp$pZsc5Hg%xp(TOGMioVFQrJ&|pl|JtwaTT5v((y`U?@EB;M10V=RGeiLimLUHnygRP}Ie2)WKE_Y94R+_@ zswM~hY;Q1P&H`z%uz~tjcvlMcYD++v?GWBQPgX%oBjN3ikADeUMILcxIu7YbNs~DU zu(kXXzGQ35lc#(JvCn_lsL)wqc%v;3$NSuDu$nCe-D+9;{I`Yfnn(tklnL4 z@X#SnG^H%hTy@x;L+p@hXF%?X=HyxzJ6D_noaWlyAP6F}{e6U>F{B(AMZcIj|C1t0 z_kXHn!CXw}Jon&!=;uE!{uzdT9|WyQ2$@cLRq`TK$y|CV5DN~5S|~+s`pfMn_Rga{ zalA;W=3zDU@*p{OY@Kw3=Zp^5x#}=tcWtC}<^AltLx-a;efRFq zJ?Bs)MRua}$z=k_P&OWZeQf~-yR)ayM1c+x9M2+)gNIi*IOi(| zPjtZ%sCvLq50#}n#&dmMWG2nh>*-6ah&}r-tt}c!hm|sfEKmyCqnsm$7wJp1V(#gW z$YhX3!B&ozf)6|y2=tqcB1KNs%aRh;GZJj*h4wR$`0-=MLT`!GL}(&W#!4F-%Fco< zt#Qs%6y{7Z03tR4kPeMJWvh>yk3s}t3gPG^+y#%yngcqf5%R%+iRd}6_fsH;-(1nI z)C4`oF&`^a6zY7S7PO9YaWth~o5>+}TB6`N1*ZZX!PVaMtX&s0Ior5KA_7-L=fAl| zVx$h2%QUi8MB4q+>u;7RQYCFO=nTh|6y@0Nug_5_*>%wK84TOtk zIHCn2y&joE7B{k7MWe`!GF&UYpNomj0@gd5oR-WoC#7-u%9U_>>R}ksxvw{G-hM%e zD`HrTI#5+_eTf342Q?9iP-0aI$DyoF9BNh72CUTYTf1M;l|1%TD1f44K}GctlV7=5 zI&EPL5W|UwT{X5|YL4udl=GxqbG}=9nLvSK>?{Z>7_n;PgvJxHCHKi7fnc>7;o;6H z#YVz#YiAOroAIzWMXN||MoNzT*#V%BWxy2HGUC?t?!CLQo{UxffRU;o6b<#K+@(Z5F;GHiIpCXD%(l5vZoQ1z-t;BlE&8p_Hg z_IYxUAXLr)B`UxWEi*=W^s#l9^FEv$TEjB%H&lhe|~kyx5tspSl?oX#o|GkJ#PKDTZ#^y z14U%Ug<8vSyb?mSnyG@F5CxO&>GiAE3oVA|lBG07)&d|!Zq@XkHRAYXgHC7D0}ARg z^5DJ1Rr>iqlm&BHpchl+jFYAIM#~1><~KUe2HuvE`6tSr>I~s}C=8Fy3X;e-%(!nm zzb*1t)k;M-YV-5(z%TpuHLtYh%05W_k!xArzyA>EMixJ0v3h8?tC@EGhW90QZ;`W%Upa?z6KJbtc7uSGRKA~8{0v4oEU{`(GAMU>oB(o7z4Dv^E1Dc1(Fbg7meBKIz2fY|F6Rprqchf%p?T_D! zh&|<;H&y}!gfU-K{tv)yi>!#nw85n7E zm3!eu7RKdBB{I%q^9*6n>^C;5i}N6(4XV~=8L2I_!>whJzyAC0zvF&EFBH5yA85)5 zK2e>PnEhwYE;vt=j`{-d9mVA5a(+lOC?Q`3bKI+d+Bs?rTQ0!rC$_3~NT!gaG>x%U z^km>ht#p=n!~vbg?>0r$Wu4olSbc`)kn>_XL9DUo@v4j#At@A~VMB}pLm`FYxgAO= zm;d|qKSP7wCb9OuqzOgqnRF&oxElXSry)pzAUZ#p;3UXmQXrw?9YGT)RjyZ1qX0Oj zuB9jq$y1)8UN1fa7rZ^^ZzyXHr9f#dbv8(CunU|bdPapiyCCoGER2?U_(2k=j>5k8 z>1^&RyQ}EmfBv}<=XABt4+2}CUW=ahCf;}b|NPJY{J*spgeP(<8EYPGeQwq7L=p^x zjY>heIB=8=ylrc11MQcR1-|l>0`Qsr&LO=T38;0;<{*Rt>oEe>l(z>$&k)} zksN{^)SP}k6Gav;BNwmoUGF5NL}7_gte=#Ks%wQ<6RV_a>^HF{S5q{+VZ+8ig7kla zt{HDil))^%-x$h;a#mBo`}VggS690p3;|djy5&7Vb_@lBVeLgNe_p&8PMTJxXjw}+ z)ffn}qo9^5JBs_X0U{8Wt-?9Vv~tGwS`MwpP2(MEW3G4y64ipo+Gta{OzrL_lXBGk^j@ zbw1J+yLa!3wH_pt6~YJM5&S$T_d~GODG~;S2qKt0 zdnUt6UdzHOwAR*6ssmiiDBC!Go)r&6F3-YPfPfqpn@#^YPuag25e@-r${f*CCX?lPDn4YM^W^Tv^Mom9`&Fg0!vpGe7%68^D(8`5Unx_SgA6Y7%%b2W zU4}Jwkn=DQX%mnZ%`}RibvkAs_75^$O)n@gMy`{mb!JgDFMy!Sqg6HOsx5W3GAi7|>0E-3g{(+3i_P^z2K4P$J|bBwkz|~gngu{9 zil)fN8EuuH>oUNlzIew7AM98~KO(SJWxjG<`TeyxQ39UX*^{TEZRJf;6b}8ncW=Ka zdUW7N*SO=_Z&IWdzYhg1_wKxzqLm}~si}_zrR+|<==&lf7sN2?h*uQciArbE?@_;c zhC5TvwGriPBe7>N&v6Vh+7tW>QG~a9!P#FbzcWq8kKDi0*ll zjMDb4tv@ge;rt#Ny$&Cy{GrjoP?zL%^b{#JdG$DU_l-6x8Il!F+w%aX4A> zdYfxs8=DTBl&u(5Q3MyJ}z6+x+8XV;Qeq=n-2}>FC=sQjd zWna2i%1#6$zgI5~B?pyK1+#(ggB{D0XyWl($G2r0q390WNFXoz{Y_^gs;RMtiB?1} z+e(jozU+HlKZM)6E}O~-jAGrKjQF0tdm@q&s6bDkTA5%5D#mcrPx9#Z)0ty<=|iXw z9YP0cw~hYwvW+yu?YUr*;TL7wGerUCr!WR+LJud$!hWpkos1mV%aBX0RiDaUc5Qdj zyxRW*^BAZS~hCt*vz(6Wk zqt8CwD>zgiqEv#a)J~7|b724Y^UsA`Iw(i9U@aJ5M%Y|x&>llypSuyk=TUq%ouVu- zw8A;8Wf#t${{l`ngu%u&zIG6WGSmtwx>`yU1r~kB<58TZ{-c>ug`y;T!#X#^gWx$q zSI(7;hIvX{6-^0x(o(5MVJUv_gqZlAGsnPdVH-+sM2i&`IA`7SGc-Pv+_1<{5_`pG7+g}N zrd908fQ;77VxY@{Iy!w8r#}yk8Z8<0q~PcXBrg5n#ODsFei@ZoFB~Qm+hBKf6uKO& zE2sa9*Of#?*4;i(KcK}e81UAwivnGwM(TO$9hW=`8yX{B+669O{Zks>7CHKVsDL1!W)(~)v(2~Uz2 zjrFUOo+JYTKBhpnZQr&qd_~la)^f3^vMQHtzPE2`|Mjnbhv5QcWxjyl|NJAIfMXT1 z6X=JZ|3Clr-vgP_uon*l65TOUWZ9wu8%C;*NZhs+_rk@mhd zuWt>c=s92NAoA}bJ#y^$SGl#JbWfZWX%{dthguqjlZ602(b~gv>4adeE2>35MI=j}AW2AuGb-zoaR-}aNs5WnWFX|FZ{TqLf~wvpyn+7^7GGM{hpV}fc*Pk|N4Rz3$q`hhuyndWAE#= zIAF*_GP)4aRC;FqYnc?Bagl=wFa1nT(|>dId$VF6Qh=M%x*lsHa+WUP0Gb^Q{b1CC z&T+m-pI{0@BBVP3JezsOAwm@SFgowO&~cEToZtOa@;adMrL#O&O4ylWv_)_8B>|TG z2mO%(iU_;xakpQK^INLoa`c{`*{gZ@htKr4?d)=}*QA*^B_eZ(ff1fKD27ITd5Qgy z?1J>3wPSdtGUyTKnqk43FQydCKaM@!aa_^^4pYF(fT-T3o({wn8kP?oOrkSc();n4_1|X( zuucer8}yPfhILPD9K~F6P6}@tiOmKo%-1bFvA(W_B^Qs0@~b_woQMw}r@n%b`dt{n ztz|0^gZm*F!$XG-MkRwZ=qg4PXVR(MO69ZW=jRIez9b@X z-qky)V;Ud-8rpT89N+`xWH|2!>OG$J^jM#*d%p^O;Q2%OUZ#BkdExN06Gg4X?j@hG z=V)KhUHB*1o2B%6Bk71i&tjv9jKX`Fnu=;q$^GSWl)Txz^@h*-o>$EO4ve zFzy4Va#c74>qAj)4OTdWHH@~`dS3-vS$K+y?L?G=)R(SMQ+`?%ukG!gmh*B8<@+{_ z{=1TSZ+5om*B80h8+Y%GVeEA|5o%w9l!xac7p^@Gs=`~p{?FgFk4f=M+f<{V>P&QJ zKs|)wDNb1A600Y5jaheS7d>Gw2RfJbX$+}I&p{L`HPLTKk(?=~S3%Q`i5*e(&Uk(d z(x(qm1#REyObF(=^!nM9??&N?Bc4Rx`o|)tEx$TeR-0>y8!ZWIY9qDJ>~#q-oC_Aa zPeEGamoYsq&j3IX%3)tOQVg5%W~I?*FgT9u%b=h>SFRT7yMzqcT3#z1B|=CP^@qmMFY3+w-hPoIR|E?YUTN(>N8PXB_T5|X_&S)1h+@cn zf~F5H0mVNm+dDEtQ_dY_n#4{QO3albETIeqRquQpER~=(wg)2*ty;fi@{PmW$?J*P z+2!Ok1$m@E5AKG8gfWjtSM7VWz9u?BJY2V`bzZvHTUXB~bMa@$s46iKAZ^Z~U21SV z^$bxe_`|o|3}lr@AUa_7XXa@R#fIW|UW?hTh@cr-!#fc4*q2MOw^>)$bNbY&BBz=V zs83N0H5DlhMqt~vt?_;_J~b)nJeE_mr9BQhlY7-cc#EZ@23f2TgANgMwyGjhaSjz} zGVDuDwCc8K^%7V>7G95mH?4v-rTA)bj~r)#3NCCsp%^Q0Gcl?9yM*Lv&umQu^cnyz?Ij_eIG ztF$KtHl3Nkp z8Fsmb0|)khMGB+be^WWVh)`-P>HA%&R6o}ta}XH^#dzDyMoydD^=V7V)vBtcJ0@jh zrhgPlu+XNrZY1C}Q2Am-69sSe;rP9cwb*O2v5WlKhpDfA=Whnhc*GPsPRRQ8E#;p$ zp7y2uC&QBILARAqsi7(AC)6&8$W^MFBDfT9J_DX}=GUPi^5ch#ne_IJ{Hp@bnVFd{ z{#Lz1Q3byx?jxMc^tX>5Jx=E*DcwT{ziM$AMEMvw+uPxIRgHNStx%1vk{c`xo$I&x z0?vo@Tdho2c~+cb_ZIWhQDv_yD%^uiC^>=eO7b8zI1GEy%2XQdGF{Xuji(qE4D2&8 z7TRZ_=b#B=qYL|AfhaWcTQKPESsF#F93-e+mHs28GZnhG3(MG5O&y%KNCUR-hzLcb z^Lv7-DNOmBOd?>KjUuB;nMhY9x20rbrq5K7h=RY2I*6=5RA6~jtGRHl69PV?5a!m; z1uQIg9@UhaihS#G6_CwrPAwJnigJFw7puQ|fl`P-Ts`AE(j-1@9VsXA4#=gMRhuvx+w_CM76%lzV4CRSqmZHdi`tipw%#?km{G8mgr}oF6e=Hbv z1QL{Ky`!)*{SOM`>`=s*zf`^ta&I~(DJWM07qSokUb`PewWVpiCq zGT&an?=wYvSJ{$guNH-L>x(*>TCxALld!Q)Pw38oKshT1OZ{bLJVcK{x3}l!3-qz? z+1pkjkWX_S4y3d7_5kic5x?!57@DLWY7o;5Jqx`=APy#&#~~H%FkFpP`D5>&z?OGq(mejQWu#ar%}01(fZx(WTYV~u0IQ7KvA!+pbgHBB0L2&IO=f=463|+ z^Y#mxVQ0=7Sr=nUjcV3v$te@zA|2^>39kOMSLCznIk|~+oV@*C7ZH&BdmiQ@ z?^l0lDb{YLI@&4LM(Y=&fPkYJ@w!X_TmN2aKJ7ZEGvbo`_lh1bUb+~32>y%zV6Hq5nXQq+MVUFMVm5TuX;k#dQRPr` z?5*Kka+rBVxdOZ2kt5H;xwL1p;pCLPFS*qf#R+C~@Vt)s*hTT>sgtMT-#TVQ&7aD# z#vDxNKWbpo`wDh4e6O2B9=4G&w^mX%{>EownzNtYSI&g@#t>v+yc&Mu)?ID6x1@ARyPM`kj zGfie&j%lsmC0DzHKX)yWbN*De?x_4Aob-{%is8-j2TWm>gImid?H4ix82HbP$=4lf;MrgP(1QY<6Tys4m*!r z+leBo@lE65WrOUrmOwTr2Dx2IfN{uGlUwR4hbhw!Wm}CyELyCO#5n;ft_OK8dNvx0 zFXglKf^&wd8z8HFsu%gSc>L!Kn}!2ApGpWJc6qC9?53PzWS9 zgC_RupJ}h5Rn8Q=iwR?NwJcWZTPHJZpF4N%E^s;EaLC;5jb@!I$bteYxRggnBh8R6 zR_OYvoO7Pp=qp@>q~M>_9R#i<%ff6Kt& z3KU|_FMg}SdlbRQcbSW;n2>Uts1*j?8Zk^&Xqf_Vh$Dxa6oDw9k#Zq&y5J`YW-S?W z;=bKiw?u@#CqbzC$U*s0U|Du~WP$qJOu2E__O4_>B%ZTsZ}lZHbYr|AV~R@Vy3Yae z>I@k~v1e84TO)hY8LyurrV`v>8_s7xBStY?r;MH%TWprnBskZl5A22dAylGY5IH20 zV<`e>aGh07_;|_lK9?B{W1`wrpgf0mH{X+=2mjb-(iwsSpRHqqLC+(7=SOOR%&De_ z1m*F}Lm3Pi^7;*!ATe>+9D0?%!ALbbSKGJ07^yl^R=;M+?JmuTL#}FHG7ac_D+8N`(*H6v=mRLm&4R=z2>DgU{qfT$arji;gKBrQ5M5Iq{?7Yw z7(l;6X`hHJt*JsM2AeTgovzq(B1P+*Fp<*t-yM&rykHpH8qr#nCRz2g;<+l{J`w$` zYoS=o=R;wRre~d7bgo_dD_8@!Zt2ZH4vx+*KmQT~hzvn`nG8_yR-}DwPi|^)z(p@) zeMtaB{8t6Pv=-={Ayw}JZO~I+FAyBO7NTd4->3AqxlOf|fkE|>i{uU_op9sEjo4dU zzhIY41|IJ`;XJ97fA#uJboYFC`C-wPyP@>A3``DvD2ofvTS1q6*t@syf^|y71u9c4 z)ySi#)aBtsK%jetbNbL%qdbPPa?z5;NfU)>D^kVIa3d8F#Xn2WS9Fd-HjmED;`JiG z`c8&M!oYxRtEm}#!9G}O$jnfdmpgav)V}-fyNGZ(+tyiIC?- zt2L-TQ-LdlfJ4ZU)N;$o#CiyBcdZORJwjhVOoo%z22+L+`hW{K9USRv!AK{?|1Lp( zrh9=_!Iz^Y>^@gAOM&SF0!|0|{JHZ(L*8S-9Cqv0&1gGR?C3d)=0BE&jUsuo4eDEn zJadY4*ZOG7|4`nEQB9yB(Ax2f}(sE+B#=``Y8*I`_ zp_D4-OP4Q4URPV3UU>=~hy2jNTfGEolJeh-yLHw3Efpq((n|)i;AJnnU2@mUS|^S9 z)?{eVhcc8t5=e@IwXG>nuDG$0!Zn-)Me_Oc=L2nNRTvvQ_r`Tj?3`F=iyKP&q~JuK zjloJoM`gFcj@3}q-o8Oj=vp_@OHp$ugxLmA2% zkfA{z%20+fl%Wh|4am@-52fv*WoIZu8Ol(G(p_0~QzYgg3`mN0B;zxA!NzhHZ!i@N zTQ+(kimH5dvw%w5)r}kf1Up?vCS%!TCPK~4M#Z<`C2!xpiTm78PBTM$J2pBbK^)3Z zhVpI6sz{!?>Fw#+g8G zIwa1h#SHeBFJHwOoZP*8xF$mxN{6M4JGw5bu5w5GfBWv8+P{DKSA75W-MiZ5E0=5k z{qO$@B^%@lv$H{ch;k68u!x?aL1s~ml-w4*w!>DO_Q=auFXOo!(x*JKE@&Tj?kuJ~ zby7TQOlPl>@h}(KvpxC4iMFw0*+<5ZGE4@MKmiszIJoK!8o{J?{@l3~t$(|;mJgGI z{qf^eZE|u?aK5979S&gWf0;>6F*;s)a`)u!+GO2?y`l7vK&cDc_P#dtVJc{K_w3oz zaecd(tXk88H*pu6|7$uBE^31bK%uB58TAakZ`u5Lf)C}a$M#K%s0;@X(P32DzI|Iv zlVS|&=LQd@IWe5YT;x zI`e6!n0DC%V@_6W&}}&0|Lxm%YG==!359ArAlQ>Z$7?J~usow_Q-azlUIqt2#`el4 zwMeEDJx@m9*6mxhv1HH>9XQal#TiuDv~j$4_ukzEvmcA}#?531y8*d-_MEb8EVd@( zMO4@hgKqNJd^$YYnA=z8w@dN(H|Niu8c+9rFzp%tHgtlqzj@7B?sZ)!GpD-v?ex`*~Uk{|2`ITq)X))KG$<)a4AMk1OuNP zy_JfhE@_KYreJgKqyyDZF^~GCl_~ z)o*E{Ln*r3zI{hJOCQ2n=Hz^9Vn055{J5AepY)0~P`@*VA3q@&(uD@yh{~Xfp5yi& zM#DSPH9UFxB=)fDG}iX}?~l}m(mkQe&LsW83C;!F4-z7{XV2b1m=*k9y>_*BJ{foW zM&SAVci#sh+DmC$Z_kkw5na7{wf5bi!x5>82p&HAEor^Ebfe#;sOg_jdWR|lB_f(i zUX6${8*^ea-@1LfcIw2*o*t}0mtJ^^Y9%;g3>!zz7pHAC1|95}=XA?qf!=*@ez;V^ zT-sxQT>LXan}Y`rBxAoX4wfhkN}wh~9BkE{*7^z9w+9rR zZtp}3;Q)w^2s#Mr9c^v)G28D*a%X=ODE43{dw-p z*+3KU_j6{~YO)A|2ig|o^;=4e;|Bq}edl)Ug%1&oeiy&f7P~*`nVTs!Oz$Y_-nFy$ zso}O6bVY2SPfG4kimD@*e&As3{)301birD=U5z|Vxm8M*SCeNyi+KD@OOohn%a(5o zB3%pOIWy?FYe8We#WcBll-9D)^PDZuWot^i?%KJl z)^DO6``Wc@fg(fr>;n;bZVrR&aF)*=?OooyDs9s|>q-OuSLEp?1D2q~FQ*&kg;DTC zghlC6^x%qY;6Y)cc(69UGKBE00RG77^U@7|03clGMENcU)4gwWKBNc&+j z&@W43qS=bW=NUqj>>)*$qI~=AL~`23lHR(S6#U)=8m>{TOkm*hcO`v)mS8+9Hj zeWoKv2TQ*>!|c$BAAYF)dFf(p=dPW#ojWEL&iIR$FT$RkOh|ISNLyELA{lKF(WCUB zMBZ+EZ+ch?2`0;mV^x3$4(t!;D+?&&a(WE~E&n+*=7TG$bG6N7*|>2$s9;x97&kgL z8hyDUZAO~&w>9k*X`1^<5h#MAXhr05#zZfEhQL7Y9F2AhB#INNHbmaF0r zxP19a7{fECPk&W6VIMN$w-dCkP?B@FE5WMZRr*R~uP)+ExrZ4TdW#@Ul0$_K*^eES zHNK6U-KWo@-l$D%F9K6KMU9xB_mdOS)+?dRj(mS4 zjI(Injq+qgQwoPDzGFvsFD!t=rO@EP!-r9&4Bi?l#K9KG3Esof&KCi)AKq!iH&q4wU2 zRL|urSJJazS37;`^j8InMN02(^yYhB+KW8DjQ)6z@};6K74IT&Rpy9_UZg$5;6Q{Z z_+bm#>V#u{;)fGacOsqkI-w+Lr>raE-fsd{mui8|96xp}t{d0ImKEx>PD$HCVbHoN zO%O^Jkf;JT#Y$CUu8Q;aojXw(f3VEE52e3lxm6ChfWcG_%KylsmNzKNFX|*lR`9g<=WMVzEvN4UU!qS)-`b= z!C-kf%gy98qRkwVsjO|0cEQpmXTF#*7hN+JjPvnh$G_MQiZ*}y3Ls^}G*gYFvQSaWp55_g^&;2TbPkV@s>qobvBU(;AN{$>f zO7e{}l=7*+r^6$n`SHSqzUzxsP$TCLS_9u{sc46|cSZ>7kUU?nywE&>Z6zZ2sIvP_ zB}ZN_3_8V_3*$UzKWpUNEq$@Ye;93$y&*tZiE49kxZH|o6jZ~>+t(eD6*>ryV;F4`t;- zSExNPZ-A2(x%5Ymm((%66#GM@PWll~?ify}_ngY$cH<--20oS?? z##kgviHUM^=RvdbEIm7-rHArt#6Gtn^5|31c7x~GvssDa3x>E9bt6R)RiRzPN$x<& zXZf7m@>mts$)b4^#C-7N`6F2P&Yn3tOu4K=1b+xz=v_AASjth;!!d(l!?!nsVKvgRUC3aaNx3Wx zI;LK|d-pa$v7@y!XU?>Z2Nnp5d`hokl9|pfOB}OC5vHx^($cq&u2j8{{sc6O^iQ zUVkM;c;?ip;BK!MEs0PF;@w5e*0ojPIJmsr79x+1ISah{@ZrNKjI);(S|4M0!rORu z{;jW-f*4H>1?OV>FxUN+a4wx62nUiJT?(8eX|R5;X>nfV%{ivkfjw1GL2d6!!_*rG zq#&aA3+K-_wJ-7+RpE~E(|iEKe|&}jW+*EY`Tt&T@fhKi3WdSoX`iVoM;lZ%CwQ2LZ{(vEgc$lqO0YlQ7f`=eqMkiKem z2D^C2k!U9(?ujv%kJEoHs`5;4rW6o|Mh`~e&p=iL5mM%Jw_1`p-JP<_*!AN4}}64Lf9N0ZK; zGrL&Ne=|ZKzIpRHoHPC1V0g7Lrf?ohLBvp+la7Zg#Yolmv>_C2ZVw|mo1L2t_wR-7w;|N80Y%5qB3JuYg{Q-uC}>iNR=3ghzl z!DhM6bxELG^h|U^a6ULa)%?Hbk6EzjsF+`%7S>L(nB(9*6x{sr=O4ABM~_B@?YAnP zzcb_cS=Xw_Uuw(A`OSV54kBG371L->={2`oK)KcZz>z^4j1Xu~ouOY!42&wA($7EB zO(C`T+iqC`_D#S z>x%>>p9tfJniBNhN##lLK>`%~fvv%Xtw;fze~!AxQ}p}v$$6EN{@Q zkG~gLf%hrPpHq53HSFEH1y$s!QzwJo_0XZi@qE$4%9mG_Nj=$?zf-$my&0@yN7t`; zdd)^+Ep-Acw$~|F2#2D=zBB0a49HxdEnx3P*C$#OfrG-Uea*RDc6~X8uoa<)I6_sl zk5i71z#cqw_)ymdArbp~O0j4<6Z8vRbNIW%wH@2K*CQY6nGhZN84dD{^lzO2tqv)n z7d&ob#lo=q=qfp4X6cY-#ChgFZboU3>iaN&0#e`4t6ZJhy7T8Po&%HBD== z!_7FX8|nTg0Ap^e94wLH_L!l2Iurx1J{R**ObXUIDGX_z478H8hrf|yt-c>-w%M}4 zuPP3wXgovgnUlZS!Z_|2Hkz>uoHvax(Norx-;kP(}OoSWU*$q-4TefUYuX$PPr34mJc4fp@tyb2zJdE^< zUh>7np8PwC-Npk7uZlF9d-wcX)e9U_=b--l?B2aA&aXDyZCxCKqgeFD&6^34{4ShZ zPB7yKk?7WSK*#8_!^yE$K&&{?xzaHpSZP)=T{F=hTNTgBgvBdYu0$ZWwKC%)cEx;e z22u5+^rb;($a$SSY|8iW;qL;Xt~VT@LBup;DPL|QEyggUIF54azKld9@%g)vP)M2l zq~I4XHet8;zkS|HYZ&4hM~Vu4$Vg_;LKssY%=fn_+%Ip_sew@!K-S7U+-oQ3EIoUcatnzMOCC4mQ{F%i(IaR)sF{TF-s4zhTJL<>**2@-}q+NKwlp|DLCyee2R!2A$Wf zYTYmdhei$sJX&w?c-RW!RLvhfdSuBt*I+}Fmbxtt*W0(BE4wL>r`MhZ?9LOl>9b;R z2nL~`CQnimap%#fSlGUTt>+6voTr@;2DDkC9`DPreJUrH9XaxSa2hCPqsY}aj8>}a zbqwU_+}g_R|8(KU_%|cr`s^in(_JNL2m_%&PH`ng-^(I7*9mc8peQG7WY$^V{jK@i z5lKUz+R>Vs1thBaTLfEw@3WtZ%J zn82mW1eL4eM)t{+s{*-u9A`-md!o{Q$*3rnvu5?}rfNuhra2QU5dp+vZIaf{^?vL! zdmO#tJk(zcXRTWiE9z5SI*6l zn0+5-*fWL_NMW{vpv-%E&bcRk(4;R6di1CpoBHHQivC8U7>c5)-;_LH5jn^O+*;iz z9)t&7<cFkSSi(*s-Tgrh2VpGOnBe>El?-);&6 zu3JEwLt)_Eq|3FpLh=+UD?nX}^E~rSF#ZCyUAr3VLg&^#`2q=y4EHmU^M!-d9`K z(tU$0n-w{4RoMPc^vzMr{Onm=%Z;+YDG$`3%u&zk4dzsBNZ$vvoUYe@bDr+=b)|bK z=ePGt-oO7)uII*xJaRh5z9?E8cLXGfi@aS%mV<>%MS+;Svb8hIorA_e9ILAgUyYd4 zfFpv#u6T-Z*;!>LC<%^f0ceeLnaEx`)UM8o1A~{pq zOc_J^m=5HWuOCw^!?xUuHWE+^2$LxWycDx3*=hXcy7 zRAIlGp;En&o)~lwfpWid=WZBjts@qYLaQDM%8e43o-X=i9sI0ha^?H8P)1uLUpkTw zG45_cVSur>^bj_&!R2lhz_~w+d6WvH0xrXFMS%wCMHljws}&pjMWs+GTG`$#KI=Ss z&npJp_|jGeS}@EM0>z-ZQbSdD0`Lf96$!&*H|6= zM-kT-;dEOlp+7HQj2a8YfE=L>qhsN)P*faRd*?}mu7Z_{i00=Xe+o#PV&xb?zB*AP z=(!oC>`Q9xWisGTpFay?7DOTpJ1S^xos7cs^gAt<%T-G|S3x$EPz5>x+E71;hf=0T z{B9*_3;R}qrC_3wlVcBc`wY`1o}Uz?zy{|>vpw~l5RL?+eJiEMwBtIv2lgMRojY?j zx_At~*}P?QZA*!mzA3pT1ageB4xI14JKWMU%Fn~IrDyl;S@{TKoicPYbc|z!Ph-&d zD!OjaDPu;C5)nb2Jb5Cbg)ANh#fwk?T<$?t3p^NnMK9REVv1YZI2kU?Q@$7y#y;SZ zT5m-ZB;O~m7tv(O398Jbd~9O5-a#@^O)NG=*_9NfGA!^rn(F zvlVg~hUI)pQ^@DalWEbBL-o0gH+V1j@n64*2*CQ{>ZTA>-BtwwO*Pe#MLyQS?WJtm zyeV3t)LGcqk*iHnGUX!jVbB@*t*an8Y9x$!w}&G^O|o%kZ12TP`+sWp?%j{56N8~a zDxy!{?`{BdX93aD0l3xQCj(Kx$DBQm!NC=B`a^r9ah6)A>5tQGW58@h+-E071Fuv# zX75svrls{%LQ4Mr`|mK294}|oKDM7dBZ1t}El0u`;@BNduhGS{5{XgWX+J3x1o5v# zI3((9wEn$JHbjlqpmo4>)u8jf&65T1@!INPPDCtV#j$jF~5FB)?dVD#t(KsI*72ZAr$5U;kt)m}=;rm$WFVBt`rCe{&MG9bIG?|c+ z{Cn{B<`#NXsbvSh5djF}@HQz7hnQl(5~%)y6PTfRkvc@=)FSz?i1H70j}U~B#t!Jt z6nk;-C?nS^3ioA?NFzN>PN}`D7oRiqQ;NpcnlQAO91%F(yL&IvEP>c1=u>)%K~O}u zT&jUG#>gm#E{~jh%+*|2N1w5h$W7e`ox`zc^!G*a<;R}X>hz;%oBQ+PpYa@Z8mON@ z=A`z8?xzc^C3N`~n*uW8ku&fHqSPBY(KatzZci)3QU?=^gIs2*%J*J~YaYa^$*L$= zbog=vl&5;95=Rl;gcXDskSXgIgV^bT~t>1(( z1k)Z!&Y5Uk#70km-Bn9#VLQCcKqs{ZqQWSQOU|c=5HbUm*|P%nGD0u8Hn4Cb$yL$Y zws${5??E6A2QEd-B5#?P@kvmxqP0=lIFgf({ z@X#R;FJh9F2*V>as4Z)$Jpy@@8fj);#J3`xXak{wz^H5$l^R$oJs^F?HmD^D(yjHa z7cokUKvbNyYAW|zk*;cqf@}M53{1!olOmw{g)fG$JPZh)XUFqyM(HXdqQKX3B-3k= zNB{8tLl|_=uF+^z>}*9u{yBPQz-DIJZ_r1B(zRfyLaYIXUyeivEX#n_Qw4WYKzZ8a zOoVfNbD3o?oSo4y^nUNl8DLB{rRY$aN-K?s^II_N1^2*_n34yffV@EFu3Wns-7w3| zWdPS}k@Jjq>qpaNRPT{`dQ@775+B6{2vi2{|et;nmg~bEFy`}zrNs+ub1f6P0`=;7tf=2 zwwng>d5Yw;gPltI8XBciUyj#WNoR=6eee41dCE=hSN@jK_|NaZEqQNiO*m+%kHOv&`?hX^Q*mMf5n&l;5 zYr-g3?_KoLs2xqj`Ely=YfW97?jW0%q85srr!S96**e$-CFT1tm@22WL8_kLFxEYY zbf&C5;1Dt3q81$*DzE*H(b9v9FJDd;?}F>K)(|HVs$8)1@hypJe`ubr7c~!}u161x zmc-@6`f@%&_j%-LGXfd3&OemY!Hp!iE|TGQjiY^1fZI=EpGzfK&*g;C2D4Yws)D|~ z`}W0PmR3HKL!h5r9nH5U9XIGWxN%gUJ##iZr{$t;m5!n(hlMi3-s53sPUl9F*`_jA zciUvXXor)N!>kn?^Ut0J1JbX*{Tg}L%z*VRND-k|D&0#;*Tobj4t>ugKR0MhCk;B` zV5oLhi1RUBC&-pFiAL2v(#i;y@AlND*C`*n7%zJ{J?$K=iDk%!a2;^Q zmW$%QWf5_|e*0$G*S#-`Q*%wu|NiIyLU&;Hqi;C5tq41?F1?o={DLVx*z@eO2cJt<~C~#_bPq((Fpk`Irv2J8x zE-!^5BGePFRmwdMN?(@BO=#Ui6g+HY(|y zMyy`l7VZovy%{z@MPa!D{!>}hrLR{+O2O^h+a9L4PKlHN=hsxcc8a_zMK9Vmrc>MN zrOTIs(_hZK(S8!~A%uzf?nzG^SjPD3LXb*2uuRXoXQMw|CHA*Ct*==z;HMcU{%tb) z6Q#q8P;72?F48M_CD1&Jw$CQ#tQFMOzTn7Lsh-!mbUaLn%H|fMG8ONq;_Fm+XYw#0 zQ;|ER=5S`-W?njvT3gHhHhapvMxMRCX1(tqPxm5%TBnCNIVmv(E*UEYG~K3t?1gBd z(o&KKt#G!;VtI70)r+3<=A1Rr+e&hpgDe?sid0hzTZ9ut8R@SDJ8Ng#*vH1htp$(7 zhOv%3W5&~&`#Ygy`kAHg9zA^$MmQYB27f1)t8PaN8seTt;&;!VJ&$zXauw^0IT`de zr%ucIb?d&GmbaL^c=2+9vQ`kafB(M!KYRbd*i@FRi{G*h7z4J+Ifo{7oO8e5-0%0k z_snzy4NcQcXmZYAlK<;hdoQyQvK1`Jl2Og^X~4Fmy+Vbjs-B9D4-Rb}k9@z)bd%px zB=;?)sd{eE9e74pbX_kDgIuA(EP99MDm>viceOS6xYtE*qRWi4U6BReT~N30gYq{< zfn9eaWYzd+L{V;P)dHDf5OP2^CFPu1u8R4?o5JB(?Lt{ce|c%7*Ot}njV7tBEqT=6 zm4$KsTuf5?wha2a%DG++b)oKp4FlZ4fzDM`KDg5)w{A=HH>ZeAFSAsh$oOP(be^Rg zEta^Y0tXSkNDpk!FwEDPe`nG#4iv-9nDf-o&_AO1Z>62EQ)wkKR=6l2%iT$@UcQO| z3Yrp(p*0M2dt=YrP3NezzjN>IH=}cUZqRL#zEaWd`3n~!AE=5QlaOHyK|MLUIh4Oh z+Xhy1wAw*jK2y6y>>J7gA>$NXP#0CPT_?;yOUc`k&NPO-B##}${wNvjJIOPrKsX_a z{Zt@(1_LGZ7_W9&TegH{#yCngVEBfuYiqu!iYH+3cklZJ#*%4tnY8IGWpXPx8S1D^gE1c|+95}gb7ayJuJ#6Yvs`r?%Uz3cG6+=k#s zVAqcLJ+EzW%eNqX(GHY8;~O02DJRYVHp+#lP&Wi2!`F;K&;_PVF-Zvljg3YnXc&jQ zy3Lv>X8?CoV$S!b>4ncPm-CK^dB9rcZhCvkh>2-BRTBOvd zohpodLRYHao9shj+~CRBvYi-8Zq0xam$<9Nq^hDn-2IrF>)jqBC)tv8g{a&lL+4K+ zq&zDw@B8;3)D9gwxX=Ps6?F`pqN~ZMzfKN;N_N2pqj9nUo8TAESu0Ws&hN=4jFlrP zB%^XBm`yLjb&n|#CxbH%`OD04epkSRDw%QTEE;dR7!CAdqQKEvVK4gG*|HMSS9hN= zXf{a)gzihx2^g0#Fa_uNv*!i#7pl)>)K$7(zkVa2J(ck0~^egFK7}QBG#+>xCRSa=3Q>{P{rp6iY$VOkDML7)FV<0m1u+J29#ObrTyXMT>4?#l^pt!<|sF0Uw5A31-_z`(kYhum8<@UrN zsz#!_!x_r8QB|@h>nx3<_01g~Uo#PUd21W($6vd{Lx8|`$t6Ey%7fX17?TT#@ajRqxirTRvx|H(= zz4QKg_Fy;4Kzp6fJ4}4-4yB8vz(<>uwQI#8q6m4?nctd+4biYx^}o*i)ah=rao*UX{)}ol}2~)%$?jz5Y^TR7di)aP$~tg~W{c>ZuBM{>gUg)N^)tJ+K942rhoaTV>=8%~s<&P_ks48524 z7a?F11Nm`08JbWCX?;vs+qguF9b;nsdSx7!BR6hb4>BM3#N35k9nLwt<8Ca%3(;ug zZo6V#MdzaQ3m1PcBlI1`Af&Qw(Xb8?c~0{%6wYfETDPudcT4yA^0^j1+k9=ItHb%# zjyGbWY)|Zml?nIrL02`{K3OMog1V&4@nOUax~X_`(Q zGJ4W$spFvY@R3OOwBwxjQii@n6U3&_PIMs8t&s|I z>V$Vp;rrcQ2$UeB41wgFIvw>G^`id5dEo^15<2xBP#$Xx&AD4v+hV|SR((#nMG+&f zAN?zZ2Jxw`BS$ME9D^h}Q*zX#ro>$kt#kAguOf8Owpe9R^F63qOvXC6eExI$Cy%a} z)|57oxHeO09wMX6l~C_IIQvhZJ`2L1*JVn``Sd&#b#X@L168pnjJ|#8moX3YES*!I z5qbt9RQSt5f>sV2LJ)<^m<|(C0zqk4qNhAL$8<6W(tVjZITMbd8Ri^@jZJtbtQ)5Q zs?%^-x2+S`> zaubk#x@@X19Zqaf@-o?593}~3o}{>>4b%VxoZEuC3l^jBw3|@OA|48;5xI9a${!2Zq+j{9LFvNBZX-1+md-tDH2=vUL%j2YHGOxK8(wG9g}+?iJH$id`DWB8=U zH+Fz{K_>+S7$=eDxUYi*x%WJWPJzj}qGp{iovR0xx|AQou9eJqMT`9C(c_35eQ!D5 z?548q0pUc3nD5P$)GX#ZmFBL~GbW$kGan;em4nUyj%t49HNzWs69y7KcJ%oyb zEWoy{-5;Q`JwflhZ-&VxyT~@#1raS!Dh!IMYXypw|LYX_81~R?LwJ+>%aM(}rY-HK zfQ}#;qVx5o^V5o0FD+En_A}AK`$Z zUD8c?9jD3yD0|CjUQao8#&l+;7`Wu;p6jRYQE^{PsY{eMauZ(edSO+$hOs-c-<_Mk2cZ z>#x5<=XDCX`^whd$_G)iXa;_9}BMv-D~&1KLe%(+;G^$S{(b&)WIqh=?wyHVjU5o(s%G zwTe*{qB##YBG11k&IRX<3*yTe0f)PfBD3cSk{78>P4%b1Z+yTFcvYMm=pvr_d^Vkd zOl=F*kzVvXXmJ0y{72M*3={P3ti;(#cT*0!RdMDr^>=O+6SP?yRqT!X^ZW3OZA32@Vj+^L1T+@Es3hJJ9^Jb#&-?R_KZU0? z|7kvyey<9(dLfa6E{B|~RO}|ApQ{>l{pE|0iGa51iDwQTJP_2n-HJZJtkb12wa@G~Rs$YU;G&1Ja5Bn$vLAw9vpMDz3TBmlTr?*N} zBxj{~Eax5$q?}c+L(Z^JQ>k4Bw4Tjr$tg&-&nd0ye`UY{a#eY}mt=gb2d^5+w!ZA@oE zZ3Snm8=`fPQSm;|0?2?oB&ElQGwwWkFfhfbcuoyOHzoJ>H=_`3^|fB6H9asO`Q5vB z?*+My;!xGsoH7xl)WPPpn`>`kkjADkNQj5D32~I~-o97#7AFH_q^+;j*7`MHLi)?O zeP&Y_`I+y?+wv^S4a=cRVT2P}I;`k!)$$CR*n7{U4x^Pkw!Aw;f|KliOXFf3)k4^) zAWhK5p@eqOi$DJGLm+tHg7nm&i^lP|^C~~3!+PYvi%RZK3H9j)4C<`nT9_CB<7b~E&!3!T&M)QqD4`vqCizIw5T}_)8*tP>qVAsW zTW<~^Qjg3gw;IG9#S@-eHkZ>fYi%HJxdUY5rj6nFYQ=R{AwOz<=r&z8^upQ!r2}pz zS^+%|+t4fp>)!sIqI5qmZMrk1R(Ay_NO6X8>}K79XeikMMR#U@OnqacZF`P;QEYqn zba$+2BXOZzhFm#I85+W{J#GG}11IC(g5@nKp(95QhoPsODEmDL+LYtPm?lTHQbK7u z;E$3~QWYLO$?5Y^if{O$?^NNN(xIo zFI4EncPHZPGwf0eosLv>m)DlWLm*-i_}Q~(QmW`$=mi}-kWPYXnE@Nd4jU4~a#8^D zchXtS47xyUe>zKw8^P=KmHkeVU_t8C`sE>B-=eIVM3w{2D`QX?Hc`^4lc&PiFx+5P zm*ni3GjW)}>j()T2w*PJ?UgWyyS^x#>ZMSM+M5_)-shVHqch-IQxJ6Mt0^i845y@@y(T8UJ28QTn?BNzg?Qw5Ai$F4l-#hA^HStUYzg&ZWMRo^#vW zwys3A*2P)KX_YM2S^;E+?lx}d%^r(FMW7HMsf12zMsmAnFH=;Viu)pB%m@dk?n$)W z)nZh#Jf$MZU{XwuGs@2(6vDPPfR(|Wis+n!0^WV`;$;*FW|X2+;Q+bA!9Snw6x|s|tmwUUS zT}^HeJOz$XSq}zOIPvP$%kbK&3`<2d?z=Fe!S9=liM*g_f+1^^d~CXwQFZYpbEUIREY&c+U8I+zSm2l4DY24%;(Tz z^8BMY8X0J(MRmY(*FS`*zB3cyr; zKYR9kp(U`Li!4f`thyEL_fqU<9(U<1sGC8T$}pKPZZA*_zkgTEyjI-ROD_L@i8JLH zk?TOo?7bm1%js!n$fYVo{+nmNCSak{6o}!<=fDqBA9^EU)RE}yY@vs|UUYzHg)XA< z6T$B=C{j4&&qALgIns{ke4s<7n}ba;J$tu}`>Kf0DF_CV(o_WHkn`FQT!6o}G=Ns+ zT^LJq*)t)5ONoieM&v%$Zi|T9Om)UK_e|Pcj)x+T;G^EQy`0Wdu&G>K| z0N+3$zo9Ig7m=ev%P0&id!v^lc_ur3_S?C0wb^uDvcpUlJbC=2wr1^`+Ue7$28z(6 zQ{n7F(;$+q22!~;ile?SLGe2}ppxySxbxblaUV$e(&?PqJnc`N4OHTOvOQ7=^=RU9 zWb6}cdPs#Kw6RewnT*2mW59!b!fr}V35XPk zE(XSYu}E9a@nV9#e`qjEnzFi-kP4jg!>XuUR_UIg(@oBy2rKW6vn3S(WpUmR^DqF4 z3F{G!K$g<$oH;dI!+sW%`7-A&$E*(AGk1fUc~Au6Hdd)l@0Lhs=^~JpL;b2!+nJe3 zx^8?G@nO)5+Vj3|GBm|Hi*h;Uo40I^Lgzv22dAg$0QDo=n$*A0_t2JI$&u-_?xJ3X zIzo5Q89vYjV{Q!M{S@h)NKtyLgX}@p7cX8+xvQdJO5Tq$?KamqWiK5dQk-mHuoN7b z8LSYBAz&DN4)9x^=M66Q@A-2v=9K?p5T8Q@lF}YF-Gi~NW=g+I4pA0#jB|5~Pf}CK z4wV2W*H@VHV0V}KLwh-l0w%Q=N0I}@1Hba{uc%bUng(U@?7n!=(m>CjzlbwSoMDAL z8=_m_UbG^v7NMk_RbQg@VH#r8iu9^te|@E(cr`Ip)=M6q0~uW`6~iH<>lMZwI&>)7 zAO}tC5wr%vQbk`m@^l`F6r~o7MhnL{fNXS20u#Nd?qPmzm_Nk2!AEIeOk zY|FxF(5ETi{xJ$LCIXIj1h zLlt@@6pqx-nbSo9Q?3Yyw%oXJBc`s2Dz=o)8tA_Y|7%Zl79RxX`I8{fSdB7>ig*~L zSH_$Xbq-fL7w^8`3Z(T*gDzlEi(=v&LZd+~dLy@^^?Cb(F^{|dPjUC5z6Fi*rzqgt zG`%SzbH$?u?fjX7H+O!y`#jVJeXZ*1WFQ?zM$;Ga7>m?Q925gZ^fls`#zrKnQe}Fm z3u7I#gx7ZUYAE<-z1E%seJ$fa@hKGXJPe>HfHxPHI!GLXOFvta2$*;fuS{S@dz0G%bQ|wVA7+HOQ zFU}UYU8O%~%VnOKor%buY)EToJ&U zbdv7#S=<+eWOTGzC$!;%j8ORO>_>LQk!-o^hYuf)K^%&oBR`UcitHWEI^I1{jfp z0>V6XO_T{GSPJ6j*vO|=y=@mbIFi$6^IR(Z)kO@0VdJ1kr$AU1gI}WuI~i;an{($J zDj4%+KZzb@J=)#Mr;VgLQcNYh9p}0imB}k9?jl4;Do_eYEsw~OQ@*wwecTVpllmZu z)?4r)<$(Na80Nn}4ocp!usubQ^Of3n-<^!fZaa7G2%TnKqt!8;Av%*CQY1?^h}^Y4 zF1M(1s3UawN`?|8<@E9CFwRIy5V6$vk~0`QB9QQo7t7`*(juJ<`^#{eiiq_}U0(6G7$7>0|bXfmb`T+**mD=U+4; zQ_Djhw6Qb6o_FrtT^RZivzwFgICtTEQlx8wY(~V4{*%|OQqtk+#l#|n6Yca?2aRiT zNI*3{GXnvB#SjIGVTe23?1G+kQ#c6S6fGBXl`*Q?w*1@EC5A>sy+BH9+)TN##l8=D zKvZ)r8TCOI{jkd!`cR-Sv(e@HrwfCf>yt#~pZO!kY9Hr6Mifqm3v}`gcklCrO3yBRxm+3g za#K+b4@VCCWP&&t>!<0`p#ijDTD|0=WC$%}bD?FxdCkJXome&yqb<|CL|D z7ay&D1Kj4MvY&^Jk#NhF&EajgyMLd`$$6`7XvwgMs5FRxk}}?uqAQfE$ekc#M^paR zd;p>ywhBYLPj?f_M{(0bsH2AslGinhW~j z)5nGR(qLhXL@JTxC=F!krSjzMRyVd@z|9OfPIi^gBJo~(rP0b~^lBGd@<4WTH?DvA zK2iiJQg22S1LA{}RI9zK5o%E+cd+*R#oq(I>IL_DH$?QtkV9kaCuS(?A_dgyo@!4N z<*(M(q)r9WgLFs)B=^Y4o9y;INlT6h1XN1d#+cGakE7j?=r)mS)&i;qA`89&hCcV9 zzX?8z;hIlQr17k)^&QkZHt4c0r7fg-^SEYzW~{9#Cs0oPBO31%K&rKIX3!<)LRv+1 z-JqlHsX;H=sNO`8gX-p987yA3Vm5u-#lVl*g$ikqhAKmpdn)2jmK3nP76cmgBSZa} z;^x&G|EE~O^sjzKWpXPwG_`aMwt%lA%MnA@S?GoGw4_n}~UMa~_);P(=bq3XI zI!7FqUiOsh>*x5QK$hoa-2Y07@pRdy+BHc&Bot3^A|49W%9s|N-E5rk@<-v;{AXD7 z?%g|IOpc?Fhm{nm8^e*lWnsLZL;$lj5W4$UuGF?Cec;YB@Ns7SU^yC2@u7mae19O3 z-C&07S(Upe@KsRU-H%+dmWWgs!+-&|-)adcc+aPDUrUO0R~HaUzcM?Uk+_+h8mQ5rMd+f4 zKhwE2QH{f5{mnghw>dE)*X3}RFD4A7)Yy05eHX?&cL{*REm61M(_V?5%uTQl&adtl z2HpEQm3QylEuyaUcXt~^FaPZCKBH9GYM^5sN&Dg+7)+GyoO2Tcbjq%xe5>cjirT8z zuU`c(iTkbw3A((u3msNH)r>||QerMaeRQIQYFmST>E(#xwhIZKRVhhaE)gJDqZdl2 znM9uW{{1LKD+<=3I~IouROvWf6f>hPmsTq*w}c17>TABd-`T(W<-=Tk^*gnuwB1# zBYNi57O7~ze*H$Ao3KBh=7SGJEt7nD)^mUU`DY-S-MIfT^5<#R)sHYEo{>Vx?Fil>l)%q9nNLl;NV8bD&m4TwKa&U3yvst5M> zX5di0aK7=2YoGk>{JBUWDu9FjojrTDXCrt=GF-y3)rv;1jHSDR84{}<{i5yGxp8JV zqx&a6pIx@$ax&)H-MSU%b#*ukLoL^7;#pRd{!MP%zhz-e{`=dnp#WT%)mGVStfEOt zlsMkuZV|ay%b*BNO3fh}4?f_@=(%U4mvUYh)!b*Q_nvW7f!QrVacH*j&h204ff-B_&4U!=C06nX>P!GW2x_b3mz}<{{o*boZlws#ELV>y|Abs5* za*@&!a_G)*w6Opn{YV5S^=lmP4l8y3NkE(!v`%cm90sm!E!KR^5P&ryx|{ z&LxGxfKMgkjUZ-in?^`I0Z7Q6IE0M4?tpFyMI(|qb@Jp_-xI0IFZrH|+NDGx-NEuA zc>Zcml0o6x~VsK{N2dGOcG}Nt15z$?ak(-fPOR2Qy$;m-d zGLrsF1h9WxiaHAEMS7KPQV4E;oE_(4rqb==JVUItxn>fr=!Q!7#t<#6r&~8~>DX>` z^>!#KQ9&-juZ?W)-aYZ4o6`q;mXzf3a!bRL z6rHS11`s5(y@gS*ONod=Arph{5WVVh9^LE86}@f%y*jXrrU;MHUrLTA zd9<7d&^E^;BM;5wO)^vr0|WIo!Qckh%)NWkH)Bp1q%B>To83#{*Vd z)D<#vyj96>h>G{_I%;$!RG=6)dZqS1zx)#25hGa{fl3u^X*&Zs)n3cVrMt5JSAPK= z{Zx|11lL45FVIToC!;}m)`R+tJGXAv4jn$+5e(l|gML3nKj1KZcDz-rN}#Gx0_lN0 zq+*)cO7YZ&RhWkgC_E`dUKqvrx-_JV8TYI>BX9ZaX>HsX*DYJa^X`^{1d}>|NqxoN zeU@&Ih!y#&SQkN5#q)9WAPrUMzmae@TnNP_XeZa!y-r>a)#6ea&)z<7=tMM<>2x*~ zGkJfWu-;<@EF<|+oL>e`WC}m_cVCRSZi5%Wj5i(i3|Sf3gJDgbQLZ8>_m$&ZMZXL# zA>XH*xgdoTc{IV?6z`3j{{%{;U@q%4cUFN@%v9*Ir|7PNYDIZxPoEC`I!ptMR-0&t zK5?Q*C0|P^sxZ(ekObr9geLmGmEiw06^2 zU2R))*{93CV;5a+SLHG#ub@TFhsO;vUhM=LJT}nE(}=X6#`88RqA{gMuJG0!`IM=s z>H{o7N(XbY2fofjb0iBB0o;5LyL{Zh$5(RmlecigX zQ7k4p%j+OQ%Fvax#>0n?B8Mx_JG%rcU6wU*$!5cG5`}UchK&TIAHp!3*N)||n1b~# z{`)MQpI2{Q2f85oWKa}DYT>e<{b~vAg*XQ)le5#!d6eUP`Ql|5b7%He^bT(h9cMlA zWJK(f^xMXb8^X!-8e=_xeWJB9oqwa4t$FU88Ick7Uqsvne9_keBhH;VF||bW6#f6U z98wjCXmZ5VTcFde1|QB1$2rc7eH<$gUPzD>wjgM+8S#yz>j^1vdlWQ9;{Rp**}PXY zsKF(kj2vEd#@^?rh<9usOhS$2h>iT`=F`YWsu5t+s`5H|^dCeOuc9A6)9VOFR4bYa zL!J(q&(O^_ItS9Ta8MQ-V4>n))T4TODPzj1aS7yRbuQG)nsn&j1T)iQkboR$5q7sP zn~oo^ja1;`Qgo@TEv9b8zl?2Mq$5Ui_}ITZdrp4-=IMKPVaB2Of($}#kN>Q=H-so+t%Co`b7BHcd` z=|ew#TI2%Ee9o{ju383!Ligbn6Rb-LeW}XlRwTpKgXX2`HDt`?9c@zH^vo2be-|`8 z-ABlb6BR@+>2N^{?mz5){>{PTyfuQs?GYm`@>AS%I>ClZbpTj9pHVM+wM&Amjll-* z3+FLi`d;L3zhzOetLuQ{Cxs?&yS}VxfVAaGS`mbv+SM|@UHhdD3Xr)o_9{6mlUpZ4 z@iHtN@Txd}?)jdyUT@PHangHf(8(8TUFwt7BJMHH14Wn~jgfP|)&JnLz<02A034a> zU=?QtLIoxl$cWgTlj;y3XQVnVn*Nu2&-B7k@R;cdZf%jXrRysr_xmz< zsi(h~V=HIy;K9RUjAt@p`;xMDK{}yfmFoiciF)Oeyn}WpkbgV;*}W8y^JMUk&o1O3 zL&`nDBsSgW{1@-gWH1KZA><`?TkJO&3S54o&Io4~IItnZ&x3{DEG63D6tHQd+EH=- za-K8#>CMH(B$fGVDfs9TT92|trlkVJJ8gFNcBgmMuW$Yyf zS}lgcy8N1i&c?~WA!wyzq9@d^Fn>JO)yYI2UVF%R8KqGUzSj5pQoaErNr~TfBzw%nMTy7a_dVCF6o6>Z@thM-HHoxC&*^H zEtG$*8*m_t=x%*&uFQd36VBJ%+qt0foGJU77ZYbiPDZUiAk?5_EqkI{5f@bff+d76_0wNCvkR1Heh~RiDBMD&_Lt zh`#D&wWJ&dO&G3Qezs6a~4@gE$R}pqLFBl$ju%MQv-*-ewL# zN=4@(KhD^Nq#w5@JIHxa5FNZD$$r&~0I41sT10vI){xi}dq=hG~ zI1pJ`x(IKBn)XrC*W{%$;=f=8>WM~M;9V9i9Z(G#n)OR~Xx~su#fM!zfe7eh` zFQ{K`*BeQHL~pl%PI{H7Nut9(G%$YsmJ=tAN9t5zq;vrESXb9S|NIm1`Kxp$j~qT6 zD6RBC9%Ln&Kb`^;XX^X2-^aO9gzEdcgsPG`9$JyUVHT9JeEHrshd9XBX$#?9%@ky@E;^2g=N3ujc3 zvWQ&}%Gl$`kcO>qC6;b+0Rq7 z6x`$@8+4HimG6BNhewsU&()6!bfWafPoI3T{`KCbqAG+63b0yXuoI>0mQYkT8_a|c zlLI#zl<}<##O7VfH-cjey4t(JW*5}HT@d$p&=RzgA z$&u2HL9<;l1wumObEfm73SQ4T{&#S^AkTR zHXeST_oPT~rMx*_8}FVnkO#^A=-Rbb!l3tCKuQ!UII31#{PN|gbP8ZvfzRcsj8T1@ zE(GuPV2@jcMl$#!STNmcV@!a15lU7aO}C~Bj%4LTAW{tq=oP_Hqd;v?jC@t3X)viO zY%_Lu*Y+LdoPw9hK1>89wzFGbByKoSF~g&^;A^0n2~;)9#kM zzy3Gi{&o?9=qXJ3W=f%xyjif5P0s`;w4#W7*nEy}!Jrc`|H~A4bE>pLHM(%?r`AW+ z`;HykYnLuv3O?;-MU#p}8hNyXB#NmZ6u}f#PU|X51cMYuHLIEEn^NjvVuh-9rC;)( zh+&uUlGtMCnL~_l6n9BU3lp)%W70c6~wGpbWa;O{M-jgNjy)m z$Ej1NnobLAB*E{S4%(U1rx&=Fy{5zXtq3Sek;sE`aK%>&Z@wUrkRFj<(aLdR0DGKU z6|GaZZrzOk>NnNqwLtG!`#edHB6^t|Pwg+6X)m{Q_F9}jXZ*u^lc7Gue@``FGGY0l>y66aUG`}}JGaYO}3!kx88%oHX@&cXvlIuo)=u$ncJX<5N2BH^vZ%9dQL22}J7KZ_3 z++Npl;17e?SLX-yaXTl55>iA+If*dv*!R-F%HL6digWbSQaImA8mOD%9?%uet0E_k z+Dh1B=Yt3LqgQ?y zByz0je{$Sdj}9EzAI67+qLsw@;O=+nMAp`LF%l2OEQiwt#N`f_Y?LS0q5&=w*%~1@ zY|tenXnGk#Z=@N}Mv6F8z=CiUcXpc@OKnQz=#t_vI07ngDxoMNX;08Nzj^&8YGn2# zv<1;fJ1w>&X)YYx`hqbjqa*tdFZ|sQ6U1eZQ2Rm8L%kSLQ|Z9(vlZf(l@j-OZ#pN^ z0GvQJ?qjk|dA9m;sLT6m&_#Dxb3}IxuTp$p>$VgR@UiUS6D^otMEqQkpj6-%23@o* zUs6BZgYmH-3gYVF`do)x^+ts?H8;-wZ62CEk|N3 zU~*)5!Q&I-5iNp@GG{tS3!S00@^32HMb!3@!=<&Pk?RyrQrbIH*Gy8YH&kBl`STaG zEy>V4iW%<1C8$Qc@E7?*JhYfoA>}e3PxF`qqf2IbVNzuDvxxbP@poFSXUO?>_ z+8RKW?ZWrPu$Sg@wp$eym%ZRl@0DQalPbvLZ5eCPvdfZ@j_ha!K}_1$+ZN&Rx}ezQ*uml=zh=X1+7*RFzDBY< zfhg9q)QzNjA~mD{t}4N$U;Y`=yCnro?8c3RI*E4iZ3ytL-?$z+hAx*&7u2mJ4}34B zF9w}qHX06FoWhY-hNe*F;0Isz>`$K()Iw3NTX#4r0l_Ib+WicxfBAZhdCudGWNEQ} z#$qx`c@*S)**1cL7X%yyD#6h9DGV2mh zx&jz;ld7aj^j>Z&Td^96HZ8{5Gi9q|1n`JQa6>TRFJcAa1+DM0d`je2szd2UVdD~g`Yh)=ptwfYWiBs(nRk$_%`T|KQBja z8w?3%5$PH2XxN9bs#Sp!42w}%Nx7fW8{4<Z1A>x0)0hN>_y=!?Jfd)uV{V zv?c9m)&~d$3549xKS>aGCLs94l)UaB-6uy~Y#<)vW5o?#v;d|Bc ztC+sGr?*WpnV#b_XU;4comhOMPZwndtBcq(FRm)+5AWVb^r$H1UNT0iuV1};^#uWp z4OMwO=&WLcAPnitmoLJ|ip(6h7XLF(K{u>XqoA&?^;y4X3e2c zOi`fnsd@jWUT8ln_b%VXsVHWguWyOpeNbegEYh?tl=O~-bb#Hl9$qf%3U-zw6Wf(W z$kQMRN80{HtD&@4trkT~oa^Wj9`T@;QTXd{@ceg8I`gx$o8#WBFYi<%aqpZN!I2Tt z5HXJRcgbP=76gi??nWm@uBWmd+jne_U=D4nNL}X)2T3cNIxYMF3a`T{N~ycIf`nC! z)K2{S-~ax&+yq;*U*Euge+3Et83yb^h zJPH=%CyJnWa3Xjf#eUbX-w3A({1-jTExe~be~_anWDw7=s2pXlvvMpMC$%yRk7wu1 zP`X;%)hSzEb`?E{cLu(`Tr)O+-&Af^tDqqIFYja(~T~ z76VWW)43CQFizo~y?c5Z-TADl?HSzVLc~$L^X6@^J!MGMS|`jG zHaDL`es4@6VEV7K{H&ZB27RLZo%2ByZKC!}ioy8_PKmTDSXGfHtSJ+lBJ551aB241jdEnd8M9!@hJ z3->CDuu~8yT~Q#pM^|+ksHX;9fsn?6W`dBXVmwHYo&&wPjaC|$A@3E($(~Re zx9;37h%)NZ9?_p(M(V@`6Ll&;RNDYG!4;q*!G#n3Z)&09+Xb|T-hbEbUE#3xQlxws zSeypSg|zeRW^)LfJyC+Zy3vU1DlLA_vk2CCRx<#dlDgpdU>kFHARA-zwJ_ucxo~s_ z^$gFYNYQ78%G7g16pqZsh)OA4Q6;BuH5(zw5>x`mJ4cZraTmL}e=!dt-9gS{tK&6? zT?7am-m6KcTUWZmI?z)p+Br?`lQa(<;(LraRyNFQeL;xoQpEj~o@$36$^X=R4!OJ| z$PSqd-t+IiJ6ZcX8TA(lI@e3cNKtl}$;>-eaI~e&@m7U`*t_RT?psg{h-NicxHGPZ zM^2qOxllX6SRhq!*;^s7v37pW9!On4L-y?29qT@wVE24L87I@jto?>`dE~d{yt8r! z|AANomY*1}&7~-lk>q5|wZWzAqAsW1t5wl0!EmBny%z<6tq($2Oek%+JjH+vv^<`I zzkMlMz^Yd@WS%psbeq7YxEMrYD=pE|m{JM$7`r1Q%~08^pzD40`iu6(s(?(-o;g$d z{o=*=vo_Y%Chi5)#eO+cSP^@v+oi$IT{s_(;kQzNh`5J7Id}eCvJVG>AZe@&EH)u& zDHj~d<^Iu)igx>k(5|4L+S3!jKp@0uB%(W!T)=UNM3Umyrg7t+8v)HK_E6OrRg39o zK7E=GBcZZWk(Gj@u_i0$HO-TyTzerDn`c}ORbMxR2bu>GWC<3cq+~&UQ?=#>y=Xrx zkPz^*Xc|iy6r@VM8bd|aBoqKWZwaAS4HTuaW=+xSPqBl|t&jA}AhuiSSA{0XaXWvl zQUf;=>i~WRtAhwR4x&@a-a3HcTj66MoU-+8q)&P!6r`>djy1=7etse*%0bi^bBg_WQmVSPr3ksKzK0Kz$3cH93;bk93CG1PFyR zhV_7sxHF)4!2~C=*C^khNKa3kyfp(2j+?AAdf;6=)d=Y+fKXUTkt$p>JDUQ|!0oUh%5r{_tijVBk;F&7MDd9)};2 zA*J!>l`G}A&)UM2B2l|sJ4H1;Jrhn zsC(eBEGEtuWarPnuGIeZ^DhgzWY5a+>bPfeDQd?|~eOL+9qMoX}pVHGDdNCXLdE|Gu)_Lvs@7+((`gd`O zMe$Ry_-ljlXLfofHjDRVQ^4q-Ne+Hd` zwMXyEJZODkPjR_kMbw~3N?n9?(&h&K8l;L`EqS2IoRZ+#``XlHs6TRioW5k_oNMTm zy>c&1p3~v+`t_SwI|YQCR^Jc0DF$K}g07;It%-oK~ri%ZB*T)cECdXjUrC|Y}$F6xI; zfgy$w1HMbqQI0fikX}Mlyj*s4eiWQ&pq0WUzvK0LInm7X89uKiMFf>8Qz>Xs6~n+_ z^kW#nk@Wt0H1&XkJ}K8m(5H?8xn19vidalhY5}o^JIb7LT$-u%wGW>@2D=-WD;w^r z$gM&v-bZfwZ6$v{KTxD7jHzh6>S$`wlW*JAqg*v-2)yqrNJ5g)dXb`9>trpTC&VLK zGgHdK&q6^AmKcQo?L1C+(;|1zDFSP%r|?XodC}_F5`EQ(cBIre=@1K!yQ<`@h}|V{ zzI%MU%N+LI5bI%X^_Jua?^)p)4GJcu02L*+DiAzrAaxMYMme!2_G~1bi$F<{_bbyr zdxQ)xB|XO@M1M!pA^G#qKLfs1*b~+1$q*5d#=LAE#_#6wyqgN23=TvaDr%9oa&oDjxW9{0t z>$Ts`pNp}a!PHjgO)5Nra4_gw5HzIc5N$&s>;(;8k9?@=4(iqvh32V7A_i;lAVs1o z-UmgX6Cy$V^0l%KAR@!GXWh!9FC|4PXv>lVYz9ekpY*e|mm8WO815a0KMwd(g-H5^ zAs6NYNUtdxtcoa}L-U_s{~HBngI3rISriqnv@s{{nls&wLFaUFs?5|o5VIK<6=c7l$d?f zPDH60O)5vTXiqYk6qm}gTt0DKS-4hI&Ne1Q5pfJ*X{P-yh zo-kHUP##;#!DreqKr_%)9D_1Jb@!uP?aTCp6p7-v^(pn?c|_+o)BZtI)I4MwO9fw| ze7~R6S?%wX_uX1C_!+)m36HgrFiIRQD9-M(jqG(gkN)eKOpJ#C+><`H_1rMEFO(}~oGaJm?{lTSs(*X8 zzl^x#(jhXcQCE_1A&V+=MVQ}G=3g@bbQ&h3SYSwnwD@}($XbAB-gwp!Q-(Lv+O zQD?O4jRgfLrPi!{@6#8Z6N4k1bo>HmPM`j&Ey`zpksQMt*Kb744`)A|p&mloQebpy zs|Fo3JefZC6UD02n+AgCU|2-~=R%j$e8HND*lX)*Ht1PYUmJtxixCj5Q?!bMjKfpq z%P4;ojq7_(ivlK|DvkcshN&?8i%G87>}M`Wh4}4c6cm<#2lMC5bHPO^r;`yG7!SB& zTphf96-UD!cm`b4AVtRediwl^uW(ZALp7hNC{Xd@{reB12ohoee)4Zn82PzDf!nd( z`Haqn`T_|t7Ukj~MQb5DW*tKOiGmyChF)SqzdW2jLN+wd?l{vDzhAFsmv60P)tdN$> z%uEL~Y7k2m)-mtF?R8dc9%yWukT?yCSy?ZbItn(ln zGzyzC5^1QC_XPpL6ea?vm_gO2jXo_E)|$0z!f+1KbTCMS^n$(MJX!)YlCR&siArTo zrRSPT%2|*7wm#Hg42w*ZWI>HmUZN2wjtI{gcOKNsoIH8*t1gHv);ZR?iK9nZy7z-f zZ7NlG>bsNC8Ifs78;K%8j_r*b|HPf>l~JEuKN&LtMulzt6$QZbh_pqldRbu0(d)Mi zWC$`=ndF5`M%8pxiaym>1*CQJpHS4I0PRK$NRhj%^<6;gHdR0U@nZ$#Y_q|Y^^~-O zBG{CyHDV0)450FQg`=fKn;Qeat5QXqoq_*-`ZS(To?IU{XX;6U{=lV69I_(fcwBS!d@U&f|{*cw=C4{jHLG>*ijI@d)Mxmw-EXy z>6HiN^iVpnpWGWmskIn-@RBdpu@Si^C{-Jl)++g``P#} z?uX&3?~S+DS~t)Aej&l=r<~n-p9XzZlr&N_a&^D_{7XQ+dW)ka zV+F!=O3DT*DDydN^11=0uQZ+LryqY>G}i+OnOVXJg6SE2#c&8yzB_Ruc=!h;XFihU zOO#tA1FZOwqihUiqiz&=^W#U3evxO-Ki8;Ay%|+0Bn3NUGy39{7S1em5L(!Z1e$UE zMoS$#?TWQ8_&r9_Ymp9Ou#crPZ`MdYhyMsHa7 z07U7cvYR(=we<6ht|GRFJs`{pYUY&FZ&&Q4Hckjk%%w=iNXqSLibAy?VwQu(jilfp zFh*K;lXX*|jq(*an)UZ@>Tot9=X%jU#AMZ0{^a@ID<#{u4u1~Ip{%yqMdv|^LaL4u zcrP=;jbwF;$egm*QYs?V1(2g>%B#K-oxwMG&2+YQqU_r#(owCw|HS^#6vDD08v z9Leez1waPufE3kAR7s!e3*2;1+)j}4(S%a9Gh7B|!<~EgYR8Wrjkd616X_>25JYUP z6um(^3`jw0)Xmhwo;`bFSW~N=CfBZAi`pMt@Emt{n=_AU+de9qMWk~)rCbbv+Mm8R zsGSG>5Jk`Gjs!UBN{UwN0HfkwM3+|5i{t4G^fO`97lY2rQ+XM=m=7Pr3v@X2{!R|* z$i8nuP@p=EzhAl-eaPwoaBsWNIMC(Ux)E9MbX|;Ln0@^%OD^KVtXI$ASWj_z>(=dH z7L;3TRTvQ!;Lxq5=BW>|h=|o1;CXMQn2*!4KRE#?m9?{VOTfODA3ZK69yZ~>z@0HY zI}=?BMBZEtQXWZaPJpXp}3Xwulu{0yef;fc6?FzT8uhgUn?8|ghAkVcinz^ zrbCAh$6OAM{V`c^%GJdfbp!uMA0+x?|=XM|JHgS@-3hz@SMtX z%HQGySxoYRz%|7%k6I8?Fc^b29&trw0C>-N z&Sya})yo$zi+=XW^$QMyifOqgZHOb*tHBU-#uOm33@xFvrGl>)k@oeJKi9I^3~u+c zckkb;{gsp|hk;XvSKNCD1{iv0Vwi@8Oc>CT9ARvOi!tTZ3;l>oLqTu1ig_^}inc+* zjT|G+I-;9{2M>I4hZ*=s4<9Xbtyp_xHCnWCZGe5{+yyQ75T-_m;!B8-xem@M8eER* z^z=*&Yr=k56#Z@(8ltJ57<9@Qce%WfzR0B%GVjzO;YnKHem3C`wA3w`%ZGB60Yrtn zwhd^g=G^)7F_c3ajD3gDcova@mZ+F~mM+ox^mEv^=HAY& ziUPycAQU^o66X-TE60q|uQ#^#(BAD$dEBMWC@1dED}NTFc2hKt7S+!)YN8{$K>H_7 z2ZnK&P`nKrH$+vqz@^pCt%D%b$ackv9V?jZ1XkO(Z~H2zuV7L7v)V~O&s@KLJ^oyE zq&V9Mvp8Vc=@x)-#m$XNZmr!3<>LZPGuCepIXvLCru< z47$G4y?ggWo;(X^l_Hl*=k+KUFnc;aL^(Uz#a$QC-zb-wC8C`h*Q2^vTf_&00Fq(6 zdFy7REmRh-RC}Jw-j3ptcQyEhBH-=Yb2StccxG1edRk;&mQlQ@tr6AeNLEJNRZz0_ zB}Uy4AN0)i%I@8}xfI!KTr_$UfvXS}=_6$^7obW=lc`}vOz+;k4?~AqbE%o@7^ zRc-6GtwA&E-#BiY%iQ{~h_nt4fJaFeDN5Xw9H|^sNF$(Z%}$jRprKUIBG2xO4l-KQ zb%U<4-UY;MOBf{MC>f(I$-B2eASucK9K;~yIl{9^d0$P+oOeEgSqpetewiUWdE&d^ zS+_QnoXg2Ef`ovC>iO;mQL-_RhS?uIyEo%ph-RDx-ve_g6*bzCF>jWu9Z7TP_OK57 z2sIH=aw-iX0?DfAohtd+H>P{-{C?q_muVzu90yh5n{zGVlb1JCsXp!IwM2l<_&+J) zbJcVhk8fd@cUPC{+6%kr5k*0uZwv4*pynPeJVD1>P&Ja?ODx^fP z#aP?tJX=H2m;$-6`PvuH^IERQBP+$AI>*xedd(PZt4PPpMEAq7jB2{Q9Y!u&0XFnW4l-#(a4q z8c|^O;e9cRc%qBZMG-;oioMc%ZSFnT5}mMod;JJwkq<-8cuU9WXU@igLAa9O_o9>H z>(A&tC|(uW&dosQ)GJ5UBuJ3$=5$wEy`RqgleCVa^={>-87Eyv*v*igxOen!)!}-O z(i@`fJTwO!Z(>+>EY_#}gsgE6wwGN9_T^3({=x1_;M9t~ozs)*G!}7|pvvRvxkcm# zPUZ9Kzn)Q)E)BGGK+j-T4LW0tyVe|S;sEjB!8#U{7ymF_X3E<^a9ABGQRddt6Qt}F zhio6f`N#bfw6*$v4RgqfKoN?0-e?`Crq)MZhsVw96b)_JGT5wIF2b#{&`H4?*wy9n zcW@Un5z5-R0r%ZcKhGl;S@@X|^xm3qh@A;aiQ-1jD}^E5;S29{H=QM~1(6f62W*+% z_t~@OwaIkHCrhJFkwWSyL3dw%vTESucNJTLLKmx<@O>W{EWCY4+S)w3G=V^e)7^>@ z$b)OZj{~Nr*@e(HAdY1(Gp{&)iiP1GMB)XVHtvgD3Lie_y>C5rXPZ zmz$C2A@XAV8<|SVfd}}l3*!w2${sXDDd)%t`kp?}45fm|R3CpDaK2@cxReZt7J{>O z-P&RlZh9svWbVWJDByANc+iTf7z&Ucl8k1s8n%dF7C*t+C&W z2}i)4Sn6j6O+?4ijd$CmW3^|TGJ#UJLWQ9CFN1?BL9v(e$oHFyyw=6Dkl@LPF7zp!+v)_l(e4ilHtkk3~M7O<|-qcMwb@F8F ze;(u_W$@zpi?|>nk$?U8Q?!q0nVE~C0JK0$zp4!ptuabJeE( zVWY?nDy9~Lapz#)>45XQnaFdWNU#6PPd^7-tG`-g z#l+bWIS!L(iGq@VtXmaph3O4U-c5r^Z$S8}q64Mi2YK;@?uC>Zi(LIo3dWvPjNQhm zjkU)Kd9^1B?F1i#BJRYs>(`@Sz8(ER_b#SSMfh|WM}dtSBw3 zFmeSmPy1y#4UH3!7q6huoB!sOw_I~osjW$=2x}yIjnS~5K7MY8nV&D_zYqADQSNx6 z{nsA^Ll*s9}% z-9-+Twm^!h+cH+Z2M5l%T)Xzm)-9wb>nGQzsP$g0sxderNIjkF7cX6^T}{p&BrrRLZz^bx{i#ls z+n8W{x=95%V-Jad#0`Q#1v$6v7E(fi=07bSR0=?(EToGEDUj-371ey<{lBPev`Wcb z>8cO17#Q#-JRAcaz;Z+()I`Y#?%;Goc{3QfTJ&8xx>1ke=FPK_ryq1#zhSC&|K9yj z!W<=4mK5)Azx@`l+Cl{*~Xom!8cTKp)AF+y4x~IyaO91@_mq ztMP2hWu$BAjn|P0C6MX+ZhKxXOz{>QD zT8ydPMdy;Vs(X_6hh9}Dl|?ix#e%b7B}G9f=jG7B*7)qJ5=DVc_J>ny#4jgv$AHv) zb;Y1txR)? zxtd5$hqS=W$B%38QZ%!9%jP)uD?wH23|PC9d-uiPJp;lPj91b#*(kajYE>@(`Dg7Q zq+~woH$t60Z>7i*>*$- z3Y{E)sT6fOOp%k(v|kp`@o?n*h$>tMy%Px-Hz`*UtTws&9HPzD7IXdPjUZ;}#B=MH zMDayZx*(%Xn>I#qO3puK@w_)~{1XmUK$}A%BD4GV9|VyMWA5^BRB-$M_`{E3Xhe*& zXL?`N?)-{SIZ2eHUTRLIqRmXYfhb9F9)nkZrh+EZZ18jk6q=n*26T3&U&9g$QG6N$ zQ%Z$`y-RCS{e1E$TuVRa-Z3cJcRY{c%WK!J7fP%LPOcO{P>?oArNtcadf=rBqtYM~%0y*J6rR@p39lj#N@r+;6cJSdl0B%~Ojld*{orJSp%D!0v%JjrXJ zCpP_`=|-TwH=@JI!G-RELF?nEh^R#1oDoo?XO&|H!8=H2&zu=52+}2}!f{b!QF6QY zbNZgM$C&R+5o-OUtH)2bM@||up8~S2i?mWXQ@nFV+!Q)a32x|_J`S0IEM$k6L;d*C zRyx8`T+YdlPHo{IxrT#+l<3bApP>`LQS-ZF%kc^?YJ+aGU%$e?ePY48 za{(C|&^j1xf2kw`_HMAS4kV}GY;p)hXd?a7r%u&65s;x=W;W)kLXp-K?HI*`pwmgw z27ygFj2wiDM{6AZGlUkk!1L_XJCfyc>h69gVpp{`IVf>SG54k%w_GPhGE<_e_>pU`8M&!Sz5*QKt7_|_Uni0j8(yDVw z_j6LdJ8@!Q3Yy2pJJ%YhoLw`-y1d*)qgId!vQ91 zR9rv_A^Z0l@_Kht9>VW+GS$ZW^N<*9A`-Zf47^-bjyU8L<>BP>Nl_R#9IbKSM$p`c zLmz_mOGM=|4-%1kus+XA8UrjIQ$uh69oN`@SVo72CJ9SLi}{P=eQzLFX{^ z%VV!r<0@!cSGFXHmNM)PCDZN&j?YHi^Usxkj@Ri?t0*0@d-twjK2+(wlrT=}8K88{ zes(noBdH~mE&X4z@P%$HCwL>Byk-UdGG9 zEU<S3PZjQgZqy_Z$BD_h_L_jpj6Vj&|#S0yb4b|F|D|aabZ7r&& zt1Lghr+6rfYW#gO!;^O=*0)#DxX6E)i;b${+<}@wgf69XMX`PS@UeF5)|+5TQ>A|N z$dM%{VRf<|s$J#WgGZoG27Rp3F6uLXDx(Fk|I62}(%D}VwJFdX5NeLl?0HsNQTg0M zK5mh@h(|Q1xT;=)NiykzHxcRW7}}AXorpb?0&!-rLz)8S`3()RW{jr4H#0DF11HoM z9j%*0;AHJ*Dx+?7lvGh2?~m|jsn=)lpC^OQh{qtEnb|Oy-Kay7qjwM0TWw4_yIbiM zsUA9@UE%!N3$+`2_Uvg|4^Umpm6nb^1y3#)L&%NmJ@h~n1QLln0AMoO#5M;E56+R7 z5E1w}hXLe-B>xLYvjZm&ovdov_-rL4l#Myng_OlXRIo%SFJCKBkvyoPh3x8;Yqc#~ zw?t+6uui;V81RKVZ=xNLdX=EZ<%8gu5>_1>+30md*I?b4 zC2{8tmJqsN$5cgREsDF;mY~w*kWG~{G4fZVvqt~r{G;uCdgrof>fNt zQC3AQy4ag?vN1S>H9WK`VE2@IqV(ODUORE_q2fKoc-EgG$0~+jTzYJD9}8Ji z?qP1-yY$B&fhb82WrO@QrIHvxP9~>JcZQUwJ&dn)P^q=y0PHSV>a@WsrdujkpOGH* z7V`3P=K9Jx2YxR*Vq>-L?3IE|=#ewHn6BZhc2X?Mh)Y8lVSBg@BtiFp_DJ_bzrKzF zXLnE8hhLT8r$~FUbLXzwaz)zs*3DZ1_jAPEm3GmJjtn~QA#XedqW>H$q*78YM?~)6 z*&O|dy=2mvF9{B^m_9aM7Qr$0qID60&+sgHytk4v$P+|5F1&C206+ckV;Ck#j3O#&6^3n32Q4!F6f}&89&*uFtCG2?6;^&sNQJ0gQCs%v zRr6XGdCnsh0*yu@o#gzSJAW>eEMuim)5YwD!E#0wNQ#b*rL+=-?ei-Dr35H)`vj4? zbm?LV)s6=QD+0&zMYN7?D~C%U5NQO1_d+(6-7kUirIf(#21;@mgoDJeehL=M@kJx$ zZpgOn+oJv;3{;6`(s;p1k^W8aA8fyAo}qd(V`pa;wsjK?g5n zDnJ)fb-M9@`o}@HjAuSL)j#(_9P*+lq3EsjUeQCCl*RGm$08cI@cZw@Ad0aiZPTGrobx{YZ+$WpgHVLG z;Jh`s-14*bz1696&L-%c5_5(Q9M~U`W5ZtQbOxe@DjCbA;5RKhik#%@wMA+nJ9puH zye`GC+$fkaf1eC@l`zKVGyRGp&x~E#9HD5?3TTV-yFlZa3;{(S-%t1A-Q2rqXKN;+ zC`EOC*XwWVaM~2-W?0+MC~roNg3rRZV@unakilIQ@|AAM=5oBWvx)?Wcn7=*f2H(N z)_u;Nk+go!e+5+TOArkuhp&+y_SpqsxY(g3UQ<3jJF}|jwfMSi+TF6n=(K<`-Y+?u z)ikU7i&3+82*%W0AuaGeoT-z z#B2N3?F%Vj1%W;%$Jrgr_gIYzCwjRyF2)>71*iYg!9P|4igu)d7?U^L3k zWF-&wJQ;(%>Cj~ZC{pOLR2E*+zP$bP3Kgc$cH#fa; zI+U^s`7=$5MplF5{?>m|-stw4+u;~dAc}0p(&6g&(`Up4(sD0Fk@%(&UHEtB%O1%U zXE;vO*GlUTB>_e=meqLyDQ>FcKSw)j?5ML~>YMk`V)-Eb&cP*&U)5l9F0gH>>!1ik zAwP6@Q3>O#U`GLIR3@j-srWA!w^^U7^Vh<0ApWgNsY*C8`s>lBLM3pEx6*1P`d7Vw zFhQ4oQuGje(8qrJl@^L0;gEA@&A`y(B?3v$Nppy9wcd79SBTc6N;sa`pc71+h_syJ z++yZ4(aHML^FWe-@Doxgm9(=Y=Mj*ByPYX5bBfzM3m%1Sbz@#Zzetm+)A6Ery@PY? z^FYzo#{K<_qS4Ltb-lPdW>p9-I?Yo5 zHO%&cgLEw2J?o}m(d$TiwCiLvlA_~eF6zBF>pDGxzoFJ%Sy@N(&>2zQ6li;WS)jp~ zg5US;+rQ8%_MvP|b69$53_k_Y%F`Hm=d*|+w5Z{V-?eLJcvKeMXRSYkHkD#iMD^mu z%kYx?Ol;ylYh{j6y%9cFOz*w|QYC0v>4rB0EM2!ZEn4;80TZYp)< zkwr@my$*_^E%DHz^NuMY@G_ohXILkMyqGhStE?IATggajLvaS=_b?zb_`K-HkDr8> z-;Oe)P}~XU@?$!0ohpEP34!ptL7AHrv79)>0O8Q)navB)p=X>d+tRAytb2CFRWH+d zr!ErBhdN18djQpAfo%=S>m2+l%r2VjBz+`B z%Q^QPiv8Sc=^}c1dU{iwQHB?qKnKcWh&q0zGG_87WXR>DZtb#R7%l1j$;Ye4tba^E7s+%Rk+t z-qs)uN$`ls+$Mt=d2{kV`fpt<@R5I)52lR38N4%I_KtKwk1aAI_dmb>w=m!diV7dK zygc8E41zHS8DbXG9kEmtNFmJr_*0N_FmCnxNfD^=$RsN$ENQ8r%}k!Kf9H{#-QDOc zP;iVd1tV=k*?`@5cEBqx=M0v04tI4>6`Kn-oqaqIo|mBpf2&r<1(93VoN>zNT{&(y zSHE+9H+Db*!r1|p%J0PlwS?sKV**|?u@+jBpeGED=Vx@gU9s<+NT1|&1IO8g#GmC!x>P9#tWFQav-E@OF99?G;CpV?y`3*Vd7fOXs-D^t4(S_ZEe6wjZ8hxyG(4 zT03`Ch@+yIb$slr`j|D*Ykx4%EH;5N6}%7Wd#}=7)U)ZWv@9!=@8Nm*yhGlN3SpJK zHsNFsC`qHwT>(cr0>%8BF0!x(VX za>c%AR;p$>q9_+4b2#ZT>80f+z1FAwI-ih(Rs|A-8kix=k!h9~z?Jmy?lzrL_fPw9 zHDF-ey*{0uXNxo5^2og+I#L_NamW?qG2|-k=Te2CJ|icFL!0S#Ar0NQTiujpTimg7 zR(CH+DT;|!kD|g?G7o}Ex*)@L4LVVy&?!x1Ht6A)yf20hiS(bBOj~lP@pI|xW6Ut< zF48hlrq>2V@{ILr;h(4&lF+NkaSNhdZQ)#fIjt-gEG|cM5=8E{!)M6FR2iColniDo zZChEKdx#H1$IvR4&5rNwvgNUp^&MLT{gcLW&wO4w!JsYG{nA{yTA!oe4djJ`4_(O5 z1AP&&H9OnV0`A$hv>wog-96}NSgs8GR1c60_|?S~aWA%jE;k5OB`T%MOLrg?3V}_1 zDdJ%)yzZb1O&38Sj03f40NZSW2qvK;s`$60NNfDUM2^J%{8X`?6zOy?mheL;mBES+LcRlR4SilAru(Qq0W`+ zHWDPM2bbh({ro)=oz88rQ>a z#QMw0a=^fs^-xw+m?uwKg*C-Wk;)tIP6lm%lU6nGb1(=dgFc%gIF+gO4w|$IlFeg|S>oi=Zt4FVQttNJ<6 zr+VEC|L;f!`@zNE7b4=A7?zw33V}irrDKDmxLZ9R5Twbv{@ZqhF;V;%#cc^Pv<8Rj zqOKr5xqWNd24xsT&5RC|KmYu*K-4~c3@69lbX(Nx+`P3W^oYu6hRFM`pZs!80H=>$ z=_DOfFV5l20Cf^{OPqhsvLFk;LA8d(x~7m}mL+0X5|xhI=(P$-3%WKgDW4Wy)f(|x3mTRy&C zteuK-#b?&nTKn1M23C-{sj1Js@0ozC)##kF97UJ|JO^Z;PThkC57VN3Sg6#^B6njm zkch}-Qdlu&@Y=Pgs+Wg3opK_@jPu3C;h+Sow1c_Os8<`*tJeql-TC8g5H84}$~*1e z)xAkY-4sTU1GZEV4a12I4*jGY^Pxipgq+un(#-whU`>~haT5u-{9xzm{^v<)zR$QZ z>R@*l*cp0ULTw}$4pAfwucEcgkf&=Rqcql83#8~`LuAgSCwvNTO%Hp>>FJ44_kaR1@X}Q(pUKg5|YaQqicSexWszB#*x@Ayh zU<|s-Vtm4?)2pIgM4n7EmW$$IGt!11IdminQi1_2VAS?=-Q60P0TT9^UcGu9>`e+i zatlV)Tc>>Fi7;&~NAb!j{rJO=(Wk0dN)_rLdW|kE?^^YGBLRVFH`7-w5jx33DsEx4@7=q6nH9JoVH+^1?i_hv= z2Z3vUwM(x4mg;b>MdWMNOvHWUT#ux6nQ2f}&aqdYQb6G+yH zHiynYN;Swb5C;8YP{iK+TxnPqYT8_jC=#o}B66#uF)%Lfcm)(-_T{cz zsbcjEQKI8#&z?s_51K<*kq!F8g!F*v7;)40qQX6;XK)f2+Buf@($R20T|Jd1ja z`^n+(x}psGD_UEM2qW0LjvbF7Msj$Az_PZQrPrynk@QCLz36718nh1S<)q)H!&6%A z$dT5IMxh0CQ>U$Z{gi<)=%Sqi6;coj_$evLBNblVsxY1kPegGb_g!hB+od6FMRMi) z(`3Z;mQwT-zbe=I`NxzxH6*U`&nW1(?k}AhDxsVn%Y;y|td>>=htrn_f zom()X_C2jSBV*HDd7aLORE)bCJ=^KNapZ~=*S2qB1oCQ;u1>34Q?xV+#yIJMM=9D3 zEu_H|!0Jw6Zw9$YI?IY1^vn#DNm2CanoVn_^il%G>=o@EqYO1LSxZH=A}z50VhZ6? z2`e`ub_p_u^crRfdsc@jk0PBbqj#14N89ii2VKHxNlMj!^Tm8wXV5_w)%DChsr3US zI0Dzx_fTP4--zPV@2NNr!i=d60i8!{+SR|~eL$g&s6U+-ofN?^R<^$>#B7xI*q);B zd*yglPKVEzTi6(8t!s>j?{6&&^B^+K$bFyz&;;p(K_sSVMPlq^%u%0>^jAC#l)1ai*-}G+ z9ycR@d4|A9R=-G-(ZNV+IQyB1ENH^h-@mHyNnclz?kTwDhbJXE5EewM8rWU5qXn&3 zr2O>~v(S?=0M)wi;B*CA%Oz*28h8gKx3ze`{BIN@avPC5=(*bOyx3>;y7EEN3xihC z41PA>DOU(fv(2nenKu$Y>Osz7S6z=^7gPJ+2uSBQ!C6MgZVwV6SA zc9wMs;QQ(YrSQSAqIl^UJki-u$Hb@PI5cV}V+gdWn55-1{O^DN6PMd-^YB4-`q0f3 z-?htoUkaq9nUUDAVPiatJss2*Ckjos#;seo15L2*>ID!3`R`KjbrHe82|^$+r(!_+ z2NBUX?tXXEkTTRh*HZVEVg{R1(z(ZZWX!)U(XvV}x&P97>SyRtf7dZIQGwL8(9#<} z|M*jFm}KJL|Ni&?t*x*ysEUIF7^%5^TjK#Ye&6dT6ns+lRu57>$20JJ89rWbD~ggK z(~`!RQ}Bp#oPq4r*ei&Mnf4$to^;NU^0q~P6uD@ZV-!X?8G8F0gga$!UT#~yY+}ts zmy&290;U5YuB!cbFj4mvlpY~_j4qTjK)O4i~u;VoDN3b=VAE9Q)Jq#p2M@M z|6tq(vDFMEGNjBMSzpeCW-v2-a=p|2vJ&F{%Xw1E?4N~CPa=;dY02zN=?Jr)v3?A=#=u#9klb&g%;tly_k$D~;*{bL_H3iCl!SbMR#yA%o zQo4*LXT~rJ<5Zmy(6@D9G+viPG&gP(Q5ORuB2*aXHB`BS)N*dy*i_1&q27}qdKVf= zNTZa;nivzhAqwC)69tFBXHtm`DN4@{ThLS{0|xPF2GPksPn7e_2TeFt+6kp3AT8Y( z_``V<9r==TXV5tdIa2OqwtSY4A3iSJ1mN^rOKDgYIlIevp&m=SV?>~shkb;r(Ma>Fk6-fpeNJ&}BJES~4vq-ej zGczm<9yT~oZ-zjW=zkT~o zv?e+uckbMcN^|*nsS2r@T&o~DhGI-itcgqepk#Nl(;NX&vG=-@UN4W)&9ter4f;|U zgO{ZvK>te{C}%=(B*ijFf|={`pMS=)Do*Uhd5Yfo4F$_y zruc-Ape^o%ct<4e3^)(E2N-d+FPvpH9l9VutJN{2-j@^2Q65F<-=;X*8#itU=aue& z^t=dmx`$C?(Q1$Spvgew&?#hwSHY2X9+yyYmHa9=R+OZ-cs#u6`QY!)k&*w}5QZfW zOrXd&CdEzx_oL9PXo+|pJ<)Qz-c!`b^HU_1KLaK5Tgn?N;QH>^iHO)i)hcvBwxYmn z%sg8z{GtRI3?wF?+NukR03UQiJsgWO59S+o>wjwqUw#o?lq8I;_XX14X- z@x8Ulr0^N|=owFumCLuOY(I$}(VaWOvG6;{VC1$%geOvj#(*YURSf$nz3#0Edc9W` z&@sUFVP6B7)_&CYjN5PI6fuVJ%x8Xjkzq3f;cUyz9UrjVoT4}Mv}VBfqetF4gW?%p zjuuD4UP#-tLzV;0QG|!nAgFIk+UV%}-*i0C7e$jI_3Fr74l*{u#nN0OS^Yv+;JR>j zrpBgfTa(Uqe}{Pv!ax{wkrBnLrATxw5)&aXR-zsTO9ZQcjEn_I%ewHg*5?AHK6~1^ zM_u%-aB6+Y@#}zm5!sZ*-9hZ1pd7}X(xO>%q0WDQ`!#+CM|~FbrMyM7lDU9~lFHN7 zf(yA!QxLg9@N>VJ!=MJsDI%1+#gH>P89-b@%s zYi|@T6a@5r?bFAFqPf#`bizcPhRJ18FX33EGiX1TV*mPBGRWPzdncSsJkWWs?UnaC zQcjAi7m=hI2)`?W9HgFT2pPkd`}6xLaUatAItQH6a~IBsQI!?q3URmN^hk=z+OZv>qJ7;7|!L+i|>}$`(dhLZzcnAk|S4{3=!ro zQE29BSIDI8W&8TUXzz)>)?~mUAOHMCz~!o=zdv(!(E}QN&(EHRF$WhPJ99J)NEY58 zLoP}&Eh-ypKdXvbuGeMQn|{W7txJx~o6^W>lbT4s!-=4nI6(b2=#1RjW$X<@ttb-L zvOl|Ah=p>2igvJ|Xibs)ANMb(=zTuWfDa#wwm=b{oEL-6C_H@lARM3=;FBWo`Z1z; zeMR>%Pdvf*MO)nVFu08jy7h)4KwlU*rd5NE*o z;)IP@`+6rFSqNFQj3!6ieNf}#u6%1lg!&-^8!2lkugP8CK89h&v0SZ72vs z3kk@%ZWXG~8;0DT6x5765_Eex)*YPt|NDRc|8NlVnxf74*w>!ZCcmjI%qE=AymjbZ#4YChRYx=MWBZb`#l-uuFy$mO&F!^nL{GCV;yd)z)dCPY* zlJeW~;ShkSq{Km^s{5}eWWqWy+}OyR3C7*rat2*=r>4Q@iczNtP6o>}I)2u<`c&q^ ziJxKA{GMXPAAk5E8LYPf2UeXh=ML6|F?zQaAqel;8Iu(Z#Dz&u(-UP3=qeN^NcEypLeP^8m=ruVIO9|wLbEcc3 zQ7UEb?cBiRa#4&b&Z5gWqN^pZ?|wKF?s2Vjo}{ieZtM?v*taGFcWRC%h?H?Hl=4g{ zU+if5N6pOUajSUGmpK=QbrPX7l&=k6Ne4`%AqS_QTQpEVe5ILCW)zO}K#bi?&Pgl3 z&zN{7m$+USNtM%}M6W6Drc*6EhZcdaI)xXdU{h75Oo99$kq3M zr8EE-e0_%sa>vdcwLkv&Bcf+YfPrTic=y}OA~+T6oIZx-;QkE}RoXXETZa8bX>+AR z#Q{*m05X@Ob%q!t-38tww*!jwRmjJf-js8#);Mq~bT7#1dM^%|we#A|*3a1;WG1+x zy>B8MO3$>I(FMu>^UuwK=qEXChtC{}b#Cm3x-uZ<#4&xT9{PUXBfMkynGB-Nl^=UU0-8F<>~V_`+bJy=FMASw4nfv z6#wk(<}g4U7NjcGh>_y}dJekWq`VFvK2#X32JcIGF_v=mBFhKKP*QlCyENP;qUfoD zxy{~mLf9ix5;aqF8LqYe;Bv2rqdf+tBxi;LBXU#K{-Lank2&W_@qti1o4)^Sbb?(^ z1fGnsy?UR%C%t2zi^8Z^(YBT6wp(vbp4PYP>6!BI7Y-HP_sJHWJ$vM*`i+{jBDx2x z@6p3Yq7Bsf4K!@>%Nf+x^5UgG!U;Ni=1km^R-9euS^gW>zM2Jl2Bx1ZYhT)x7{rtI zISMCt?G9*vF4AI;qH_Rp!g08F_ip@IktIW~&ZJSgOML@u3#dTCWZ%a_1!Son?TxgdW zR3qQKSWY_grYjnoOA23G6&RmUbC^VaE>WIs-KlsV21o?L$b5(3zs3Bz&*wsmVooK9 zd9i{Y#z`N$La*ut5ScPS91zg6)~hPLY`p}%Y>Hs^mxU=VnZ93zdfgI=acaX<5XFdi z?Ujq%YSq2y3LK7gPTP{-^UR_w=?D=DA{QL+(XW2#ViZaqI8Z#3s`pyiX$%JbC+&-Z zL1##GT_8K_zN&gBN0PJj;lt%H48AhzBYG8WdX`QiIVe+gEbt(SJK=mu-KhR%{21N8 z|Gt{mZ+5{cGmTEZQ@ueah8T6G{o1o-{e)4{oirky@@s@89DLUpasXQ1e;mz?#TWua}Z@i>tjeG~0^HC3`-!bv`y}$+Vg=1uI3llyWy|HWka6kByB?rDo<0 zZ$(6YipWHK+Rd6d713v#6kzr9TZeb)(qBp*yhzhDtEH5!+1_*IFc8L^v19=2dvlj7 zplW$##a6$aJJ*mOeJPR4Rdp`6E4Qs!i-F%0^%OJV#E4kHz8nC`5gf_*#(5Yk;7UkI zCLHp76nwP4EfS4!sLq}_8)%4iW#}eL)S*+c&py+jwnV*p^)gyTb9%u#`f?^1#`iI} z=tVfj+R5sTukVYvGwrQ4kGh6M&a=JIhQ?__rK{hZW4$jy@yorN8EI*i4?%heDz_5( zbF0IV&c%e%2S<(+D2cfO&awao5;9w!oqWDW{qn0v*f2<`^ozYlMAi)%iW5Rf1gZWC zGWY~J!t>{0V6@I55NQXlV_f8tKxue_Tj{UOQhlG?PNix$^5!iRqABn~q3XS}w-2sY ziN72k?Zrf%Q$D|4g&FK&DN0cs2O_sd>nHC<&Rn!D+SSu)P{{1&*@Y3bP=Otl=U_Yx zbpi2-dPJNqPqY1MXUd;ciA&hXfT>f!y?^rLcLUd^mL(Vd)qlP!Bu?pITdW#$PERq0 z^yPvf{aEtta~${W*%NEN*mHYrsSCMn`QiWk`rk0}obGjQ=1a+=2MO!gdEk)7?lQCBasJA!@ppv6v_WHI;r8WbjR$@;%EM_vT z_sbWzy4v0dvgO=CA=EBNw?HOD=H7e%{{5kt2UUF5`T2qez2I}0s5wr~mHfK1;yIjk z67mN;T*+C-N>Q8j3QG%@6Nt{^}05a)gb~Dw&2fX$O~j_OVECg z40?~nh(RQZaB5|-tV5J5?7Nwc zc$T6G9SEH=)_E>QT$_@pTb@*L8e>ASDZrEFP{qz5Ra=)t>re-|VCkCdynHAXr{C#X zE7^Q43Y8!q9JUOJqLA%0-`r2J?}`keO(MXLQG-yhrv<%kI`3)#rltz#Gm|9kDn|`h zC7(wQ6bA=+&xPNAk0QEehSSe0o>RnzArQYh2ogfm4Mfg8bnc^QH5th7(mh-W=o&&N zMdq9c4x-)Dx~*Ne&>7-;oDE7o7yg3g-P6;jPj}r3$pOJA>hG^aXUT*6fwKCuGp~5G z8!|HJi-^O1rG&`A>x=_~L7FK9QGi^q*Dy=k@2>Qvk>|fx_8mvQvGfK-088f^n9zO{rMe&)|%9UhQ)HVvmpbMZ7(h`KB2f60??Dy{8i+Yi4 z&^a{`jKfTccXPsokT*w7KLV z9E;^ZMRM|54*E|&{J3x#mx4t2x=%$J!O1=mimaVtxn7I>@OU{hIva59bz1kjY0zUh z$}+ji6|uztmV}N_fSKQ2-l@L!0Ax$~+f&8{)SH!({nmC?Ohr3g>y~&gXJY*jb5V1M zvV&SLqC%VnZU)B2iDaa;MoM|K(r)D5*awAgv$HcT*Na5Yc{1R>K#&ho6m!*h%ak5) zMmbQqP5tX&iDi*I4jCfY@zX3Cw@7n$dP zo?+w#3Tg;E2N-s3N+Vsk_zql(d~ z>nCf+lJQ4MGsyWU5jlhcYZnnIKomYTm;>IaP!1nHET5-2k4g3U7+;7WnkK{NNocVhf zcqTrURKCR74TIi?K~2jQpU?U+u(`FCAkxa|FD;w?40+0_kL8G*z5Am5vzb7{IQoD6 z^>-BTjwYfFmwYn@JtB~FIgDi0gG$ylP%?}(}`y1M2FXYSv-A22H- z6N*b=QKv;!s-Y3gXp_r1az@G8G4?x?(%aPl*%>Br#-%V+mN&|q#)#RCo1)_YaZwBn zNf9?9B}k76XoRDsW@=fK?Ro@mZIv*E^S?VR2dlFP= zvR2N!`Zq8=L2nwx3`-P^czxcl>RJRI(Kb~M>d4WBKNkfNZc9#-pYoGE9EKH6O&oypSa+1Bx)kT6Hy z$b7Ic)-zPb`zHs_*_S?%j*O{qX}_Uk`+G2+dL+G(Z_1!Ml)1umF`>*A!hshV4ml(R zK*%(9q<>?iz>dGB+@ROW)R`)6^}LD}T}%p4-b%q%JAK}wPUIbO#lQUYbFJHAf}QH& zEEN0e>#w_-N7r7a%Gq-;7+~@$(xIpcw8-(<+a#}N&lJ3gG}JVFjB%+&H16|uThZLP zb9aHR)xHgDTUslkJ_MQ~>ZbJVk-T>n#3x9C{Cl+6q@{Kbs_lQyO_8IW3TLriRMU^G zmf_w#68FN`;Q1~a&Swd2%=#`y$zHz^fx=jc(m}T%gdAh%Tma+Wor#FvYejbqCvOl7 zeMj=OjG7MX1pN5!-CXU%`wt7oTr{6^S!RFhOFxhzLyTIC62nrBzLz74u@oMG{^jZw z$aGVTPF4M#mB(Z`PhO>dGiRqA@md@b5FdgaljS%(G>i4ZC2w6@6~#Kcc6PQFfqJyi znkVYF4gkMEK)(mS5r~m9Wq&}wyX(#wXZG5)tD)$@vtU}V`pxyX;@=SZxF^z35U7)j z5VnZsM=emTNw=L-!>Mwst#&>l$S6o|E=57`MvRe9p4MKxdKnPAS`~fq*}+$k2x%M{ z^x=?^L9Y`83o$U^a8U)X+PzV6l_;eh?(cSF{nYx}#LJ1Oa^044`CVf-s}OhY@4is_XY2m{vF zq9eEcA{pLpP2wRgC`cI{W;W>ZXOwy`C08d`{-tDl>z&M(V=`xwA~W;+zrXz&Mhe9E z#mw)yw=p&poPX>{_gJ}xK@|C^u0|>Xkuqa`COI`f{_x|%8c8D|Jo)ka@53P!J zH`1KKBQYFiKt}-gN}TT-H?D^sX0SQE zW*R^)q$2W&Vd*Aph!_bGF%1H;*L8!=A*85Zr^R?15B{cPbn^ekqBRRtr_g3lD<>7i zMZfm^@}Z_G5r!{tv#*b(2RvSqOIVi-b*)=B83txkS|fd=-7b~^x5j^GYJ4C7}N`+%AKi~_Mi z>1~D-P^2!-h7$JdrRb(GwB*vIOEH6h!uOteR4Rp*$%qKLU8$+16uwH47iOTW0}q#XXMji1u2vAZL;+d}X%&TGA8W7D<+C0X zILMQMM^;0ul{e^imFc>&5KggJ9(bbq9RWx`3_4MKqZLpAS~F(_(F-!5r3M%j)z<$+ z0T)skM#;a`8^d*#y#64HVn~q_hgS}iK}F)SvByP8D%4F!`{VK-F;>^z#a?zOr6{Te zg|B0-!MiXDi$BA&Ktec5qhhN52|eMwNE?c(p(CPl41TC*8F6}78f1EACQ@D~lIK#& z=}4Js!o8t}$DO2?rs5oY8^kiYHJ(2&M*p@t%2d&yG@e=#cgI@h4uO@BWgB$)#~U~P zi3(=$4Mn-s0booR5yssDAZclLh>4abgW*9Oy^5-MUmTR*&YcTm0>TAhi52yYzkmNB z`fn*XhL=GPzV!5&K-k;2Zwt@3N*BA7fWYuxYw=>ZGRL-x;xP7IR*Vd{itgN^B_Cv> zS-*rMXBrn?fTfD@JiC@P^#odb79-tZ%sEV(b2mas)-y~}jO)E!RE+EC9xKkew1G&s z*NB!gCZ7tuq1f}>?kpTuhWbR^tn-YZxYPLc^)BmWUdrf?q`MMzAXoqXYLF;MRtpE0HD>K=z zV`~Ujzjpn4{8=>AuEBADu&tpNJd+}W@fMVo1NYb8e}_`{(+rTqH`${cozEwS&9FaD zM&&~Z5xW=#OujDQYcLC^q*I1B&qZ-hl>;e=3U^bH^l|%Mw~vRelqy8vCyYk(~DVU^uE+q%hT4#9NxuUxayCZ!Xogpbe z8x~sYR>j%n(8TE2T0!z7O()f4vSz(8SAzf{gjPU6d>@j!%~ZgId{p&~0OHiilQCEa z!nJ7ya_Su!bcz=F09blCg#Z?ZvQU8xCTit<9kAlieyZ)7+7-h%y2WxZ_N+ zF%jkCO?E&i1`(P+zkKns=-=+LfaIV8b7e71F^z3wuuJueOoj4~_P`WbOh1^8)+rE{ z{5B=z0-3Uwi*kHUErlFOmq&zQjq=#kVTT4ARcu79MN-iHL>pp^>GnclNgY8^o?uu^ z%CMN>{Q2ji9wO}v`d8GjtIYtkDp=ScP9k7;ilcfq0+rsM$wv<##wg!x%o!j#ae6UF z}R$81dYhW$am%^?+>G3vM( za*9c<2p*E%DLp8-bIv&g(#qD$Nayd8^IIKYV-LIr#1Nv`Z)wY*$6-lcyTUE7U^l8) z<&zaE4)f>Pjj(;CaeFlwNGhojrVIJ(#r zQ7(FkBD{nM^phP;#r^vAXTrJA_n#4hO=V6u7qk(V<;>~R#q8^JF^&23Z`w#!gGhTY z1bMK`yM%(>zJ05bT-n(}{QWR|%M2@M??%DPpe=qsne(3N z0mi@LM(A8bw>Tr*V?&U@xNG~+HDUEW)#ewt0ZfNvgkmaWO52$gkt1z}`dLC+EHlJ~ zj^Uu$Q&78l8qoY|aM08@UA=m(_Av#3Iyjo8ggvvgk}m;+;6jSRx-KScmfuqg_I*k- z<+J49*s8~mpERYm?zXgi;^cAV5x@xDxOgs9D6XWCa=TG8&S#N=oH>uwq8&eWY$(Sl zBI>wupCL+}z5vsto&B%x(w; z0;asZb0hN*Jd40nS@c9H-p7dyqS0D9LX2^U^bxWlcFa#(G^%XpmHv z^}~E8Rj}NYh9X#b?Ct6Et0dQg+OPim`7$NLK#z?VXFw4tBsEZ#J0BL>4n?RU&fUA( zD@5+4Y-^u&uqEFG1qFS8gvnOZcXDD5h0neT~6OJuL zybtf*$IrKudIDq1%ep(@?0&WD;ANQtj315(KAJgC0{M-}HZMaLYG_ zv1zq8Z!zIHxj^#pU?Brghy`ZQS~2LuDh>}AM?r-)k?Q7CEWQsKlb69e?m#I<^6%Ar zST_Z1O$jIp$tVDbSJbql$Ay5cNUMZ0-;#*Ltjn9XZ-SonP08dnEYW%Ns3(UOqA*j( zKqdvm=$^lDAvhdPbeK+yme^#>DO&vGxmHEgrUoN3-qB}r%YR?H8pTg?r~#R$9BTo& zZVnW%Rbf1*R>BdUuN4F^DMcp`&>@58fl>2pGZnr1Hs$pPDIl`uK09=>8wTAzb7~r~ zd_n@)9(4v%5zvfx<#bGa`%pP#dP#Z)3VJIo}}Pla+Q9+N?sc(P|GANl?7?MHaj`=mZG$`O6nU z@M3KoK12uLz+Nirq8;foU%YlBMI`;sqi1mm%w?9_kK#w8Xp>^n)4cv0t)Q-o^+5Il z4je1j=5X2SIOqcYW+)laY|L}(Tju(w98V?WO0khho`cznWJ=CpcPt~;>@<6AIbx80 zHs)0kv5V5HJO_Ky&tFK61qWnvAIO%Lh}R7uoUA{uGgI`2#eMXrwL)2heea>WW)suC1px*BH2YIQ-8n=x2hWpnjA*D#A;l!TWOx`O zcVvIsUxrj<=}sW1X~m#xMfxj2`09L|4PT95)Wsll_YR>*HIFWE!OsmPW6*o$;J!)e z#@n}UN7_+zDAmyJy@P6SfUK+Y0F~g()Sr**|JH)A=}(QoPl<7%4`NeTAxUlHY896L7tv?UV)*%i2ICvV}UAa$Nh8Cm2L3>%Uz5Oac4y)ys(bUc7jj@?=|T#}aH+Uo8jgm*`ok+`xW)4dbV~X6OBWaNr`7dRoh!e}q13i`Xa%Vi2SvuZSfSl7F>_n=llN8x&S0wS7tTN( zj0;wjv?ama3PL$_&7M;+8b?&5RrD5rDrl4IBojE{y)Y|08|n2WR1WQ^o;y(-);!$k zel}NwcZ&g!uEfE>QT5)OdWJ_;_PRD^$kQEo#HBKzHJP0%OA$|w6fwMY1>161& zo^``#jCPddEy>CFuhD;1DBpUIr#y%Yux8CfRKEJmtl8bOeZ+z6eJmoiobc=b|Tm{PBl{yeeLZYuB!a3KIsdco7Crn`wte#r|KnAJjp;yt86WqDxF2G!;hanscow`ZX7OaS#Qw2 zr<7`a&fXsOitubHY0IE{C=UvHs7_yMMIw)$j0P_}SO0lE9Mzq+cew04A!G;)>FW}5 za{)~eD(GaZMzP4P8Hy4H|NVzxocmCY4bA<{lO+U{4L$`?7}xoDPrpa;cn;6R6U4Hn zYFx|FVl6P-n~_onn*RH%@`85d||(1%Y%osgd__SBhY$lsT<5HgaNiAeU>n;> z9SP4V84D0JXhL3{m&_AW@YODC$nf!~c-!DO2Z1r&o-W(km}J&oKj|0aP19?Gx@{by zh!5rP74(2UHv$}$kyz@GM{G$OjzNpZKhY#tk`HR$_{iO)&+gwG5bY6C7^-Ufo#SG_ zDb*|@khP`q?MH0`r%c{7@11nbwX`0)_v{JeLVoc7{LlZ1??VmlmAv|Wg`A_v<#X}N zx85(`iAcYJelv6leuvJ9hzw~`fu0pIUEzK#w$An+Ri`^D-=2RjNA1#947B;*+m08y z!S67}uV24OcllGK#Jq<>DJezJKSQqf*lVl*IaN0hs9Vg0?zqqVwnQoNXJ?F;GYK&B z$gk9JkX8iWJ%fjl?X!%9{&n{|nfM6VXf*4<`tIMmcptfrqW}HRe`*I)sv_%fEOEDP z-}>U`Odez-A~ksN_t4q)&-vO{=~EXR3R>x@m2jfk)=E6nJQZvyaX2;Gs}5w)vuLT9bRs8IqSFQwVr)oX(o9jO}WQie116ts1 zMY_!Qcct7Z(L$IUCTK$L&M0iflSqvS);clhoU@M&(leaLr2-_*8RL5+ok@M?WLb#St|`OU=*zx_Y16C4=L>C7g&}O5559d|{Y7&xdEcVZ&7P^zmN&8->S! zgQn1Ul7KN%G^^lXQwLL+ye6+_CT+Cwj`fq-hN0%U{!lr@+R_XRklWfkyK+T=qG0=? zRV?StHz&msOh!$1`JSJTA}~ke0GuiTUgtAx;sS5h;hVt=axq*G@z_hw-j=0O>YC(^4EK zBJ6YL&j;d4@0rB5zAmui_(5-c-X|q`B;U?9eSq{-^8WJkFDuTS?EMv` zfV;9m7d45b`az69vE96RGg2Kc0#tyq*Q4sazpK9O8rGKqEc(( zQV|XVyuM6VRYgH5U14e=V-7mMm!Nm&XJ!eciSuwgeR*$fbjVlsq;!wp(P^U6U9R6u z@m%4q-h=}HfmHe4sZ5nq(!K@B)%K8+m{^vacCF0NmOO$x<~ekbPRj*u^^Bv~`=nI|S3y>gJmy@Dx+XywJXcD0Il17-k)@3WeJC_r4LM8RoC6KX0NFr02Bjx-Qxw8o zTlo7#R0FS%3o_GV*gce5LV8?m-2o)`(vrMJ48DRT`N4j!TlB}S|Js@CMsG5vEJ&8g zT#Ov0!I4_c!fCV5Y9|B+B5D!{$dRK>Q9N}#ACb{i{w-)+)7dz7w3zQeiHimW0vE_xj?p76qCMsSsO`fA_Xg*AmtJBD$@Q~zln)8Va(kp z!(pIx^(|(t2oy$IwrbF&XSGPHg<7hRk%Rtf`>U7(A1GPHp@tBuo63PrkgO%0fFw=I zL?Faou~VB@xcz!rp;yj7t4tM*`{E_}x8)jh3nf39FX*s}XHn>K;rHL;nXJ+GXU|3? zJSfJTh>Ag0IZZ+wPVH;`5M&ObQ;=zP4l_vzkweGk|z%V>j4s7{Yxwhrz z(9uao>tb@C6lIApDYexu_O-}G9t=h33#8dfN)BX8L0hXVpiW9TpJPfKlsesJif>&Y z2q%N{Aod#~dE$5!#oh_Z(9d`FOf_xBpqBg>@e}56w921q$vV$E{jmhtvwszy0_1Zth*w`8WyAA}8gCv)`|{F((6O zDFcE`hHpdgEDta2=un9!Q3^sCcB5t7y=!6W)1XPwGBg#2E9WjL+?%&<)%qcFQ>Ra# zs{QfD<$z)p3UL%amH9Pk3eYTN+wc5Tu$UzX_}BC2qNPPd>71S@(S+rQsBnKWWD0D+ z(OUCP9AEzHu4Ylp=DZ@5k*{O~9;JvJ+Z%E3oRLmj8~v^pH7Oq~Yzjret0GM2(s@wJ z!SE>D8%c9P%c_`E;h%0HgfCf)9~A*dfVg%|#|~{2U8eiP=vMcIqkQ4_3z6D_096A- zclpkpyXo$&iF1PywF=Ke3j}0p8#s+Qh^3F5FYhPqHYh>oz*!gVTQ1b0A>pScpBeIA z#U+3K{8>bqJWotWs3Qj{8edF6RutoBgFcg@$IGF-`q6^HVE_2T4*{3DM8Wl+))w^d zQm$kxj+z1<`0F>tAe(MF6_^)ym1{m~hg(|YEdRPvDCS(#0BR4Uu!L>ZSt!0+?9Lf% zBTp)&vsjxec1P`!40w@Z+!lP}eph9*LOES2D^>h7Yy!D8!=u-7BRQ%cds2>2UM|71UT8C(V^rjEr_)*1+#9xc|&Gzj9Hh@g8RBSYR?f=%v5Qo#H7eZeROde8e3<3p38UaYEF+p9>M zQQow1@#<&ynS(&7GDf+MMB5tWt}ys&f^kJ^+thT_MluKz!C*pmr8kk5<^A>VkT2)M z{s);=`g73cCZ|oR@)6PO9= z&@Y2Nl94PX6foka`dlSlkS7>|P^UIbHH^;PbIP2K;rJWou~a+LR7&{>Fc@@SW94Tc znLFP6c0aeVPxH@5(p;Pm9SLY~AvOM<&jY)#yVPaQ!+(GKHU6s`>p?Me6O7h6)ENhc7_)zj75PD0Im)BiP@sDI> zge{@Rv>`H%P}BP1J24zf>shiVIn9Wa=w%8L_wL;nIr7EQHXK0+jmTN=I)mFSRFOev z>prHl3=vY#(W>qph?^?{ba}QfLL?EBdG6i~@(mS*avd&GyELyOX)Yin?RBQ#8J_Xt z`HO&`pdia-#u~|Bh&l&fKA;oC6EI}q1e;k4!Zr(f_)2tlykwG7B5y_%5 zWylozY-m7eqHR%;naX2rUt2rq2kwBMN0-#(3c6x145M#}SeKL`gi|B3aFf8+5EYa$b#~c9-<` zix=a8R$>%gCz6ZvJ?7@TqiuYwOWtb3Qih&7Ux6%i^Ut(XjH&tjsj zQZztWp*k zK9B*% zddbEe*##=gO+!FJ5e#uD`r)-^gU&mo09!riBN;rwC%`agW6r><&{o*PVbkGZpLcHG z84flhsY)2VDPHtJ$*VKekCXA%f1V?1tYT2Ud_EaiaAiFuYxMl5XbmZdw345vG)3;t zH&iND%f~3@+StN*SM-#7kO{|#LEpM{Yi%UMEsSx{!zSm=kSI}cD|6XF_n9RwCRQeB z{7CI^Z6#$ztDG2(q7dtAC|8kze!*ce<~()<7%un^-+y2G*U!Ji*H1tE81NvZK^?#_ zh=RDM&z~*)one<}ecj=l=8>$dfQC89oJ!Fvhf__$&WCi6J4omZj&+9NtAgKMZ!dx(XasoExt3NErRN8=2AJ!zaDF)G z(ti|q44g`L9b`X};THN%_XC0CNYAt)5Lh)RFUkscH6C$>R!~;dpvz|{|F72;??1ec z99JWX&peq2A%KQ{kQqXkl@!lmN)YBSN>M(A@aO|NIdrIYi0;AeDsp@dCuyx!)^LdKq`ZH1pw{OuU~+9|Ie;SVr%zjN zJ&Q^IxD0+w;;Yx2{gzcW=;h##IyM~mqkeFSV=2P@zyI^!VZ| zW9u1oU9*6~dO;Wm ziC?gs(6{uY0Vcnuys|w*}2-%9z`MCj?)KBhixmvQS2A#oZ z)H)I5F;T~;HmX%0U|r;lUzA%HS@6Vv{_&?EJ%ADWZPa;n%DZwMqld|8n_LD0)FLo@ z!@GCy3FYEo4YK4mnvW~6yR--e`g%YjwV|{oc_LhPj=}BngHdh+Ng64-r{R2PkinUD zCaZ9@sbY=b>(vnZP{R!f9p_$zP28VSG{zWWM)oHiF_i)aRU6Ms#DVhu>4zU<7?8*; zuggL?e1)^10w8~%F4BoE=i@aJ;Q)Z?mC%ljt_@BJ=L0fBXMT6`yV`drPQ>{(pI}g& zwbc~W95Hak(4fhp3tTIb8S%~&MCawp)lEyTszGly{ieQ_j|U{D9-|evPR8M@dO;q- zH^+`e1O4+CFZ4Cv2cFE`;^JDLKZ_>+rbqqj=bs}dBU&e$OIY*yfE#o|**$yq;h?)e z(a~B+84TrvP&Ap?<;-j-J0~K(nLiFX)=HHzw2z$~=c#&zbVwYeSp>K>|Lb4>6^K{0 zdzNS!Pe5~!SEb{rt*6d#oEFh3)GT+G1hd-DW}S1w8EC19e6O5Wj)VE#RcKu+P&s(U z->BO`N8IcIV?m4Qb_EF#@zpRJb)tEmY>fE585+tESOJz{> ziys?!1wa~&PSwokNgvmCC@U9xFP}5}?|q(SFftnOvwZN|H$~&1Vg%_-(c`+}RKI-j za*-FZl*nNt7v=G{y%hJt(Xa0shj*i%_?KUQS=_7O^dpB47rnicQ!2XFTqh8yky`Kz z3{k4q`ij;#03h~8CAHR$p_c|UD}9iK)1g-X=;5OuRjJq)P(%bn4DHWZqP1_@Q*WYo z|K`{ut?Fs8dv=JX63V4~ZM9@o4Z6+Mh@24%l#EGM4B&TvzkRDHw7+=qQbRHdrK1tb zg9rVKBEgfcEkly>`evPr68`w}&mb4&1{k^HS~r{c_eye!T@_+?A5T(L5*vN+eUhQn z3bZTFDj@Ra`I*|MQdd$ySl*cK&K1~t+icNLCP=8s@U>EMIho*a`~JWG^FPrGo099L zaUU(mnAQtDK)0MZb0$UD*8)Y0*Qd1*(W_-=_{}E(wHl9_#r-0$BFvd_z0_Hyryy~+ zQcgNE><3MB*A)EV3XG0@tnO7WKs>F<-Zj$aqnBbG9+ysFVS`$W()B}t_SicdJ+^DL zNQYzp{`bHCPi>XNMst|zCzCmhx;;I4@-&*}>+CcWU7a39`G^C%ze$r53+a7#b~p>U zU2(0?6U;6Tg%rWd^H3d>$r9q)PSb-LSB@8 z5u7t;35*PZ@x44H5NzoAIy+eL!GcdhIf>v~nJvz_l|$s*>Uplym9BH@yGKWhijQtW z_oB}0*KgD|ZrT`)oaDRhRYq(!ok3BzMlxXbH*X3?y8Era28tNBAezObXcla$VN5G$ zRNi!Qa%eL-`>|d-NZhvf_Jpg zpWV4Z820R)F+C8JR<6dx%~h?rP{a42oa|LEyl5Wex#aPyE9G4}tY*+QYDAL-No8T7 zy{Le91-_#o7~^&~w!9reo>r1`vyEQm+hB%%oLv&0->T$LVf>4$i)18uXCsD1DjNGBpxVIwCtut+|JtCU-gQ_W& z3iK-Fzqie9OUiD`ibsx8ykKLW!J1OSI4eGvL1x5EM1wH+e0?G)ZF(mg>p<611nyQ zyU5=<>z(c;_u~4T*{dl^cJ^K*gMRSf!NvF79DU#C!rAazO4oUn@04DEqA5w!)>S#1 z@3)IUWkvq?KmP=|ZZ#1s&m1{;Yck~Zy*$*>|Mk~jwS$KaMvCNI(pgGwhm$Icl#H`5 zT9!oH8?8P|d+laAFZLMJSw)r}4*k}UU3rRfa-eXF!SEugAc~#%_S-m2%>*Rj=OBuP zz1q_vi7IB=@OZ)B4rHr>;R<$AYf1?ygXyHjsmNgS$&*pb`{$|Ki%KeYm7*wDlBaS= zDPP7MN)dUR^mimDb;G?lkcc8CNm0-lvSScH@k;d?4fhzz;D|kA%9LjXLg!e5zGISF zGNhu|X5w6^auCI}D~yP8P0p)*Y5!vp`*I%Sr$L+GU`ALZLlN3rD{*!;$#H(`dst-g zJ<2xc%&m`W;{*`S$G+V!f(M|(W4mX{nLPIO(Wrfg46N25DQOaeNOY{M7f6MHSFc|s zo&D~9R0gdEG(4y>lMq^@YDg$7MCldiy?QV9r&gx9 z^{WW`X^Py9iKO&f-MbVT<1HU8k6rye=gj%^rGGjU;9=TJ57ISlOh0o?J9f-1=JeYU zj)BjbZE-@J`$ekTSRut7NRc}FpxSjT_T{d@dla+fU!}A9Hsz&X*S4lys`|*&?!38m_T=wgTBZVAf1PiA5LZlz$i-D(Qhz;3JFc5 zZXG#@fgnWWelMU-#t5Iq9-C}&Y;W$^)oKo#ZW2Xps59E&_gCj$28TzfTO;A*Y92-B zMT_T6$#I`7QW@@jqIBZ>EmA9-q53tjF3=0<1#A9Q+47gub?@H&DC_HVPunMmb###m z0q-TaS&I_WCX-_#c;dv)&leR;t=JUJ#h}zXckT#I#cDkNj&ta8F&NvCk4mr&XkvWx zc$71lsSNtoGnuzNvP>p{@z28)IvqtQs?Zp~Ms3Hlr))rOAQH-_-TZmCg;GMU5GlQT zHy;iXgDqEn@9y1#n~?qs^4-{Iyp~~qUt5R>r`r*M&3*sD{b)h~dfE`5u`wc88+a|S z^*a#;MKsP)4M#B{Na~f%5*JxwwDtlK>4rE*CV^?h^)m$Y@%wgIfd^lk5lDjG60m@a?0P@fb5|iJL@J_`K;4_pNaPX>EF11BZj~* z-u4v3;58gqZAy{qBF&V}!ct1|ZrQGOSBs9V&$xT{UO3A2BFg6^gQpv%{6xB(fg^{H z#1I$nVUKV=tj}sFbI%7?N9}yQ9=M-^D`*QuG+&$REF8R|yQQgnDs$R7)2bLaxtw0; zhKdL!i{6vISZmR`>$Q&Ni4>&Z=S6GS7MvZ3CB57Zn$>OT*r0ppJiEd>-Bf%2@IAY+_92FBjt95UtszVG%>mvGQkTOT+QMz{yOT853r!y&Scx|y2a#iT6f~31I zem)5M04jqYQ18&tFBdLe3={#uSPC#unObq5_5uBYlf!uh887EN5d>t&D#y{Y%hf#^ z`?wZpi#@W|2zQ!->7lT_+;4)WwK68R5 z#V)4YPP2l0o9y`UZz38z^Y`D&j_QnjU;1pGzT8!(#(cE}bayN2b?=2zn@wxV)16;< zznI(SMT2s{dymVKG0KK<@f^08U;d(SsCv;So??Z@fZ65U5UoV%pGzB9>&+lNE*niy zTF5h6{0^dsjAv%1%fA=SpuJ-cF+jeT`<*kA_83E+4KzjXwO~|do3ot7YrjsZF);j{ zvZZk|$+>heaHNPv3c?l#@o<7=vop9OL6nV}(75h*Z{Nip-@AA3lAk4Da831dtQl?0 z^`{L+Vse&8mq7FA6ONE|s1EChNo#LjNB#Mrh-rD?-hf-zH2<*i1Z{wPLR-@)WgDf1JykDeI=)Sek-IJ`y zb>c-kKM2Y zOe;!AfWZsd%U??ZJ`|0=E+?AF;VBKHEZraF2I}Vg$Td=Y&;!IlsRipHzdMl(I#2r5 z%U5A+DQgONvIXzD9pOBv$>eD8{;fACN^49gOg*I9O|hqZt+tnM&tAWNEtFSQerMAC z_ky>uQnA0r#)|c_ZVWh7!H^aENFMxNa-1{qELUM<`8zq((q|$Rid#g@Aml#hA~9#% z>#WC8$d&;qf1LU=_O16r%CnZUl__P;{n&=0sZDiynHqTLft>XCGC54nR(>{l4X&ps zF_-IkZ$_qyGJ#c>DuE@Ol4D1Y)y^dt`9#^%9pTB@+`4sen#gdjCnu*qLCicq>wtA1 zWY)9}UnOVi;e&^vj}OWBR}c-)S#V@MaV(Vc%DY#CCB{u41p#VQ(&!0zeekH#K3gj! z(k#)0X}p-Y1{tzviiw4TKUr@@TGHW(@q6h{r5+A8qf#9P5&4L80G>X57Llk8*UX?3 zhKzDS=0U`Y-s~xwV9Xu9$W!d!z9ymGnDd?v6oz;$)~;xhz1WM7MQi0M%L#@m*@+S6{dqP2g^&BPxra18#4Sj znx|OB|B7Ds@8i$+K{TOo-9Px(pVr1Yyf6D0yet3o29*8(^S}OgZ8fDBA^IY&iv#A~ zv7=wFLTSo!qMkgAW0ZsKj&rBqxabYZO)YpPngz?dGF-^dHf-Dw!&)*_O_6Dw#JD1l z60e_2dyIp=f8YM5*J7X8ugc&==a3!D>ZzSPCGV&!`%h7!2Z9+;e(1jf|^a z16xX?WPLexW@Y$du;jZvUnPcl{si&NGzYRDlpxwi_!aJ3(BMoTT%zm|k|!m&(o)3L z64!m_&fQqk_mSRwy1orMMFz@p==WA?NZouc+49h_1&iL%3sFAv@Zp1&lwi*7Ob(D2 z+^K*9pCqRwYRJ>NfXd~^trdezIa&|Iqhj9kqu4w=A4i7k;10|>PJ{d&62M(S| zN|4ip6)zvipf6Wl(2h_Bo-IX|$swN4&ZLAH^FU0J^U(;D%A-5|J|jO;9`4+RlR~vV z6o`MnjXnmWp?DZgkmz5@n5?$sd$8`c(g?)#IXT#74r-Y*?ze!sF|_yX+Z$`DCpqIN zQkEaxR@Q_Q3cuRp`&F46cdkX#pnDFueI@^%4LUJ5L3(T{gHHS$>Llv>x=AGqL+%=V zzOw-4ra$V--WrK(l@{Q1DL3OVBe(9|yC1|mF^C9OD>6HqoY=qqiao=TnOTLdZ=FcT8eb`sXfgLZ z4x^PkG72Bl@ma6~M+0^dEoS2qBo}qC$>Uq^VogL2jBVt{(_x9$lnHeJnTvRN|CA&n zJ(AYuMj7qqaw3Pqx@s!JLLmK!zQ)J5Mu)p-T$3WI(Q?p@FRXovdhhP;l$C{ZWkg_d zN~&y6N|F-D-2p$!GnGo%|Hn$DnP*2=6||^b^p=~P$oHv^$PuI{V$*J8cf{-(-+hG20yB8 zB!VvW`fH8Q*YVYY?!aWqUgXV1n@Dm9ITVOm&PIw1OojNip3ONU#OOXN6(E#Bgw0q< zJy3lhuA+sj!4;1pdZoM;%AB#TUZRU&yr)A!K~Z*cWgLYVw6UpRwDOo4OA#YO&QSot ziFUe`nqVNX=sokQPzW1VP%if*<)&}}vRJc&mBt{-o;VYOAko)(`D70Kc5O9~U$q+f>JGoetCQAO$^&V&eG zuD#iClPFZpT-=IUPVzCPO3N zTjW`nS6FfI7iwLFgjZ8?bW)VpJdS9gm@YLMhXPcGfRsn^VVR1!!JG^P(hvjfi-MC& z-kl7)+%f|%;*)peAc*co;?_-$F*B7hZXG_ab*Q4uCPhHe8=y-Qu0&BvT3Q`e(5kqv zOvqZCDPLZ1jLc0&Kwa%>OCCY`QO-48x_PM%s4i@;hUzHg4fEsDe z^Z5(s<2rC0=1UYuy*HwivNA^fN>aqfQWVcglxwfDrQN$|tA65|^{`J{*&L?KP zGr{2?u7v~4`E+(fmEKPj*y=u$-)*&-l-KD1mmBR=$$71aYZI*wnz%2-WR8+&#Zk)W ziD5gf_Tz^Za!3UaQZ$^TYRg`0*GP)SHRp=<)+5D)zd<;+pPO`U(37`^2|F{@e%-U* zWAvs5B=TCRFxGi~yWZzuR^&a=#_xap9{D62U0x5wp4)IDm&hqI$RgidVuXmoQoI^3 zB5WQp$lcFHteW-kinec`3wRx2uE?vuj|dT5=@5##9IUNdS6*I2VK7FVgyrgiTa}zI z&59cYJX`VpL8H-uYwPNl2Pq+REgz)|(t3+%M3T+g4Ht&>aFEjEi2JGJtSiIqi}8wz zigZ2INX`CoADj)PN?#JI$>ZBGHY9IA6Ln3JxF$II67ae(s8IoDZG6CqSlG1n--a^Wr-f^Df$uNfTDv1o(=#a63kmJNkj%iiN$UJ`h zByx=tF_(F==pkITDx|m=`)<@wx-QJvCk^U(JC!_pnB+eEc@Tp2@$aQZ<@QrN2hXJ{ zxmUmSCqY6=&q}!unA7kPhzr?QR(}LTcoihiJ+Fsdk$sdFF$79L?rFb|&7k&s_d08=Q4B?RS6hqX+C^LB zX^_MwioU(IqNgzfO-XD|3S3l2abT>Al;ycac}2608&UQYPIq#)Trm&p6E(c zydfD5zvHmTtz))}q!5in%;kwBOrL83d3ioHuX0*$Wq{lfbKf~~NoTM_#i(av; zcRS{qU#h)I(2_dpuTpMG4lWN%k&EORi#8Z>x$-?x3bL&nWqCfxMGgoBFDHp%EE@IR z8OqB86ijXs@x`dp6hTc)6s&2+PakO!y~$3|V_f^)7~JwCoB`Lz!_GPWW*z$45%*`_ zd$ns;C&D;t4Vhb1G9chOd4DWo(6oHuAk}wMR`c^8uVEg_k;Og`^+8A?eUJu z7bh0cQvTJR6}=B-umlR3k(7F|*AXrSHqb+R;y?>cfQHgLS&>0!)R2M}rK0TeI7Y_s z(xuB`q(G{s^6c4D47pHdF^G~&j$AwAB_b7_f|?m#5pr%4qw&C&vNbK}oY8;%wuqj$ z&ux#_i^ONkHkycD^6m2HqH{Ic>NcaVw1z4vPhGG|--dGag%qZlg{BTM$?jz_LKVzT zF?t`-pZ5l_TWbwL7$A_p5|SvH>q4LR?a7g#fH@YO2>74lf=56JD38Jb$8f^3mTMlw zsEP3wRc0ufKu4aX=bd|yQ+l4GJTY?Fv!~A@>NR*M_r->!3}SC7 z-s&+xC^;#*rFlFYfKH!NE~_J|7<_g!QsGcmSM1xFCS_`ZvYq9)X{Zz3K14iLS2~`y zMkqP?+guZ^E1aCLXoQkxz;vaWD+v(>*|VakV+O;$`?a^}KIH$g-B+6D z&J*3BRVTjv_M_CrPzFcnJi4eJF{8gAZ6b20tEz!*YowdndX|y*x+|Lw^dv+WR2K7zk$KQw2nk;*zn>DT(#(X)S zoU%g@!6^4Sze`y&S}$L|3Zv)VwuO;?5KSl$=>j%Su)Ds{;6=^@$Xufe%}1;y);9%e zJ-k08BUq+-32Tp;Pz|LhuhZ}NY!Q64AWfg+`leH4u2Y}aGN2p^L?;oKXCl&CX&XcL z8+E+NK~R4^St)bUs~zFdYZ&vlWeZ68aj%=6o{GP(#U2Elr%RVF1raRY@6(dNCr!X0 zdX1jhB6&&y8{NupqPG&?@D)L&&o5zrh zVF6v9lbxb?b>Jda5IO@aI`e%| zH7`~~CQ5@`fXL-l892sRJvrmIp>%%a>jR!mkh(}nq%CqO#vg7ihk|U1I)1RXzxy4| zL9~`lM*Lxnp_~tgBXa!d8nhtATR`R`-C$X}7lz5yyIir5>x!+75-$^rR*DD=nt-EC z`Ku1U(F)Z5Io5#4=UF-vjJs9P`&`Ar%qs5pp$w%R0j=Lo!2#V!ztM9DzGF$pY}m9h z8rSslj5FqfoQ07>S8HQDl*ZDPFzIb{!@3;M40yVoNneah?pC!LB1*`^P~?CR*MiuV zj3e0enbNdmlo-WrG0E&+$#}mLQM<@R$wth}zV)Pbcm7`VZdXf-7_~b^Xd-%)sZD9~ zgBtyQd`^m_<9(80=T*rg##FWB0BOq^*O3goh*qR0r~hW-o7#g%4`NV+*ZF+ITSVZ> zs3bT=`J9UB6p16oJ$zV4jN@RH0DEDOM(Lgbrp=417=0;inz<0Y6h<5mNPwaN zQoXE~uA|+M73I*gSa4-VjB>lJEk*B)4k&vyIxvB`AC=RYV$#=!vFKTx`>N}_##kyv zlB;HT8FmpWet23p zM954%I`^u$ux}Z9!F`BE<;*)WeB9L=t zjCrDY&bw$F?mnJ8c^Yjjy@ySvl!H-4b_07O z)5%FO`10W3bB@E*)MUJ$96S=0s5%qfM8TX4P6WeH3~ZT+f6Ld)b1^gwNsz75DFeqd zp)Jhlqd8vkHn4F;jAQtR{*o44P^N( zZ3!0cv}+vkfV$aIv>mZga{h;767$K+#u}O_Fq90qXxOAQhF_Fk81Z|H1|Ezmnpc)2 zLYDUine%8x=eoi<0p4fhrXoTE$0`Z&xvyWpDW+dd72_nmAL9+CMKD9u+qr#57=Jnb z=$)*N-;}m=O+I@hAq8=*4<3f2z!8BK#L$-Xdm?=X{_52$G1V-WQVpej;S3!;dNlHj zAb8J-ROrSmf|qL9D7jnOo$cFayV5*s ztxH!Tbdd-zi2>|{9N~8uooKGIp}%mXs9Y_vawJg|1MGgx7RCt>MKhS9+;=5w8_W|- z_VgbZN|ZlZPEjj*W=}_%+r-3{==4Ux8!HQb#Tu4lWxN^K$pn?V1_qh2wH9jM!SJAT zMt}nVzW2L4S3HGc2+Uj<6ifd4)$8zJUFV&&$u(>78FF1dN6uGi9g?C+OpH0>zZ_|C zy$S=2;O5?~TPFeqfqK|495s6w&jBw23$jC^yQkTxq z{d@OAA2aMaFgm{`R(ee_J7e`bkQNU8%f(?@{^Z~zR0|M$sc*ZIL@jcNQ(H&&z#>*@2^(!ruUrXoiITJVl0h%>Rx+o~ zb89mAwU;9S)%(5;!Evxj#?y5&CUWJBG-9Gif1y?sE`Wf2_V$v2?;er0Tt8!vfCr{; zb+lq9!bx$TuhZuv{jCNeEZ6eq+_mRovY3N)|NeufZ7o}ssS;KB93+>r4fz*C>6Jh( z4Fysb_r({7;=KNKI{kag=3v(2qR^#EP;40;%{d8Mmwxs% zAr3~5OTR_%+U2}OSIX81W5GxqT4rzu<%&cHMn(_=IrF}*CnGpj8oz!Eqo%+7ga?oe zB*V!`$+=O=n&Y8F&6G0{ax|4mKNqosg&0~!@X}k}Y!X!ucp~>@*_oM696!DMj!7FN1LC+5P92|HRL+ z|2azwW%C~6YNww{7UrI6aeAv%WAf?qv*1w^V`apONnPI=0 z=psF$yjgnHblq#|r@7gmdWv&2?ceSHdv?f}Z6M+Fq`hbO&)z+?&qmz0Qcng&<0*TCB0iK9 zaxa^l;J@6saWfD(DLi`Sb14(G#@VO~nykIR%ZOj;L^kG^-+qlslbI9<3U27oenIZJ zl+N?`GC3Blrqt+Rd*|z;M{EC_JsV?*yXhQu6*tpxgThAa61B!Ki}at($SCou^2NtI z@nrlcTnz884W~%|`fyiC6C`}2Nax@2dd-A$GywS9Q!0#HS6qD6vl4CMm zyOxZ$-|XJle3IKif=h;*GoW4TmSoH*2hlSE9k|{a$i?~``FKhq=hYzt z43gimxnDnh9ZEG5K)iN$iaH?!%a#2>4P2*TE;eG(BDQy>f(7&bQOr@+WX9+2+O@mZ zPl<_VX-{#CIBgst&%?U;V!$1&$+GMWgME;VxxMFJR-^0?8YD6|=RHsHThHPijYPI@ zBMp}Jhz3vgP_#cz&hfX$kN2c69A-t&OUtw3q0dZb?^2aalxu zrtg56{hiSO|K_%^9ukAeFfwAu4QjC`BWm3gIg(o`irv;^7(g$D@d0UTBm;&9pBKYd zUI(%OhPC((lLIcg#>8Zp9cTvs{d_UuOavpx&PW?`iq!VFB(;f7N7H7Q*;Wkf5b=YB zHzh~GS}D^2+txAETD z!M~UwXKADLE-~zCc6PBphkpNA$1OykkGG)%!Q6N{QhB}ZN~to4zK}v}C4gE^v_y}z zY8UlyR}H!aXV?O^G|)sp@^m;vf_IbgKz`%l%3D&NBGhW0$fm`X-@U8<8`hx#p~nc! zvTbXS#nRt-QzCZve_f^i0=D0B08NRjIG=uSe92hWket)mu9-ZXw zgmGttC<=AxdVecXllKN$XSmoV2Js+-FjVbsVELWG>wR5JcH1`Fvq+Dz;nd8O=uE%5 z0djcc+Wtuq4iZhR+=spx@=KR4MMEBhLq#hAt*Es%wO8!fZ&F%jTgmgeT?QMGzDiEKbA#wKR+3M< zDN(|j-m?lJ`&>@=t^pkE(+hFGqHs=CHTtGIZ{51R*s0U`b{=|JNkWe!9c!T0jg%o0 z^rb78g45K^Gu(Aet^tc2w3*2irD)XxFlr5>$U1xE=88#P{U6v6M6;3CVZ0F*99Cqi zxw0J3A&xSzblAbSzQ=Kix#kXc@+QG2jD`*9VewaNNKtX15b1z&W^#TW%H!TsajOs$!*)1a(L|_Li)KKCvZO>kTb}O~=^h$(>gRDbX42R1vLp$$P85 z3Wcs@BmOw`XYH%bgN^9w^Eu>2IAd zVHqsScr^^VgX(Y-G}*W0`fCl^^O{nZf` zksC8K=AL7cU9Euh43(%JP+SGZi!(Q<;(1Z*_8B9~aekFjK}T1R&s$EE3GJzHAD}#= zW20++@)d;&0pmC`ph%JoXfx$1GRE@%veLiYUXkl|X1kfg^Ho5^ycprSfGWbV^I!%Fh{K6W9WhOB;&O*rI_P}D5S zjEcRfN+2TJ5b8e}`@&7T=vX zS#*kg_B?{5DAVdPBC^&7o6wU6v~av*@AUy7dY0@`H&6iAeE!0P*hkViN_t5#VtYUh zI^oU8akL^JEQ(=18933X==a5o)iLNq1O)2RTNUqJH`6a{RuZdC+?jvauc` z=Y9+2Z8o`Q!8!Wk(6Mr0&O~i@T7U4he(Vqu&q|79&;;6}tdp@e49GP5Y zgG7!XNQ5cX1R%U_8Cq?EH;!+LC@Gf`89)=9n3S4YEB&*TfZe%$Cy1r^BO<&bKOPQ< zy(+nSq?wW1v2#a4dG5u2BAiEO_Tr^Wf&T33BLByF_ma`>ULk|uCWmIN7SZyyk5ITMwGxSp>wL{*0V;`kphRf-cmR|%gT>IVWU`xm4iP{Q zgj(gjiHxUY=VIC9W03RcbP$B&X0#A}TbtcJTl;43v5#`m?!jwtU=TT}*=Vht&SZU6 z1_zy!Rc(Ai2VA>$Jt}l|?OHnwbUG-eFs1-wEq6N1hOt-s5Z4F-(=K_LtcekWxZW+kH<98*DYb{@fW3 zJnIXCGN!0KE#NsUSBF_`q+AZD&OMkwBnSKK&C`J8L;;gqch+8SdL0l8_Wp|mzblQ) zhMa<=04Rh_!;zX@7oKp@z-KISql{8+%@}j+lfhFLf9~A5=mkB9!qLO7h9s!Kpb(^_ z7+X%~=1Qw%Kf_y*9zsn0E5Yz0JgK8jYU?Td-Mjb1KE0j(K6!X@kvQgzSi^4D(RNFm z-Pt?rb2nvMYkFef#!z9fjw_X%VfAQOVjw*+H~ka*v2?&)5Td z0q>#eSTpd}oY;3th`S{{Gw8e;o1S7}+;Uy|rqbh$5|MOZc*dfO+a}W|9d1#g9`BK3FJT)>MPr1WUCy};o;I&F%^*O|J(IGg6ScdXA;Gw%Hg3hakc3jLgN07t%Sr zT|02#U|ai*A(Ng!$dtFe_V(>!a~?TNGPQLwl-D2`bgh&%zHv^aP>>@zZlZ73T5r$| zX2NFyn^z^)Fa;y$)B3(Dxe8h6u~P}z*pe26 zfK=JdV6JBdopQ0spQWhu;QpdHLX5;q8=j(20}amN@eG<#0b0#_wYdNvngti<}XTL(gQQQoEN8-6saW(0*y6UZ}q!tMJRL1b}UjHADFXiA)?Ac zacKQ(k1{O0|KI-jJtF1$k|t33iEmHD9#nJRPAcnWbQ3K4Fi<_Ben#w@uaCvgV~CN) zLCI;~JphePBE6D~6vOSlm7clZT;YIgV9ag3QzKH!vj*zLt9I|%9j}4(WTD%z{(V+1 zsp8n<&@hzM(zZbq`F;UKb^sxeYmFR7?v>}@{P}ai8sC|o#mg5jdt%V>yPhUg15q*e z7m-&F`ght-nKa!@@+=K0Kw|BqZ$a)G$5Mm7;M-C|4gNB5;Tu3`!0vK@ozrFeF6^5Qw3yo={jA&1+e7 ziPIsF!}#Z4r@~-~7M7|{1{XSWt)2(Y>{C4ulHw9|R+~S8@ky}V)7}RAuVx-ykbv7F zQWGRJW2_?-GQJRoD$z`;rCN@p6PR*N%9c2@H=f`8Qh$+Y2(K~=`a`xk-pz8wTn`m4xejt3km@>W|Y7w4fZ zl-=c`JRxc&*9Ot8*W{9+tV}2dFe8I58uW3W%WpP6oSb(Djn2IwbXoVG+50 zTKy11Hp-O4RH>LLYC(%M;vh!pB1^o2BZm(!w(@nq21Y%M)aTkV=+aO+Jj!i519WXK zQ3hjATdib}KY#GxVXQG!Ejt1N+Pw*m?^Vx;bvM*F^4}@vbvJo#Ig0M2=9uz8cD4NFMF#8QFK( zx zxNsqO7ma=|I@V1OURsKQlyVA^Yf6ts|C1h$oP{`_`TOsv;L4pqvjkB(<}Ls?!##d3A>Zg-Pm`Ra==VvlJ<$++iQ z=w@QXFV=g%MoRWdIMnLiA!L*$n7h8GHbcy*UhbTW>X4F-CCIaA-8>gCd-U|LTPHBI zF9T8%NmaBb%7`)dA3d(koETi5 z$w&IOhyJJM4c%Z z+LW#}VGubb@}%nVky1J8QUdh?QMw%CmzO^0_B7;7i)Um7J&j^ChZRFzD(S|WO+7a1dlU4&_R$yC2GyDw2|2O9v-}^ApQj)F(v#Q!>ecJ`yJyfZ zFLlsFR4LMtf3sIaarP3!bl~8D+MVPyGTfN5W9eBGV@eN16d7ci>p=KM=tkk9s+0CdA^$Vj;fL6sze|b*OH+=R+iQ^6NcLMuCFiGD`U&%)(ETt zr#IoqjFhMyC?EG`|Ni~8)sjFJ({+M}^$v$5W)h%vcvtpg*iyD5g>oPPLWR8Mg>Ifh zq;Ac~y3?#S-;dw_5S}{2v65D*Od7?h0S@JOCvEm(+})udKD2Dib7G~3gpHMm!+!GH z6N??xIx*%KQeJZZ-hB}%i9`=1#lefcb^BK2;I?j^sEww7iwGbBn4TkpxEP&E{j*)b zyLb_-YI(_hDMDh%^V%X~JxHkzV}+$|wr%-fSfo5S0$yKK9(;J?FgVKqLKO3kHPp1o z^@^C4o~b4|cI?Pvyc}xAvD9PYP=g4&6_Gw!%FrR>x(4Jk z&we$YMbTor=mWx0;xrb9{c1$zbfdCE_X0Myb}Dng??u0ZyRip1ZX6o)wIXXjylfzO zUUWD5qDL38iLF6^qbTIpg8Z=Xq0DPp8L9hoK!pR;iQ>T$)-Z?WLU5cQ-ICLl!Z@*@F_e&V%JlG>-q!tinOxCN_qJ#yE$!HNxF!cEUJ4Nc!IaL&C z+w@HAH%=*byFT;nh-a{4NB2f~_ClN;rBbI-uKBOirz4F3;^kOci|RfMCO?Qft;ce* zGo>xS^~;Gp7<1m=ysmX3&Wf~KGvVyxJ1~|S(5!|?S-6H*={|MClOl92NL<=*lkxFQ zvB%C|yb!YtX0$m?uR#XH?J!yHb3)YCx~xls-fam6ZCOhg1e2sNHLt*AZAB<95G&G| z-(63V`xk?Tt&KK_3_BJm9)pfrj2dGGIu&^`_}b>ac)5C0^|~oo=)e5>OYJ}D>we|= zQsx9aI~_H?Bjsq}vQyNTUnW#zZ|!)BNUKVdBl_hRUqmFOi=8OGRqZho_u?OrkDMKj$NXYvj3Lji_j*2OgPix;MXG-%OO?pJ_)<$6T=FQvToQX9g zL)WVXvFWjoNqFY(3Wf3d_43}mQX0_~c(6Toe41+YPrDDGSCZ@-Ygb}Hxv^Y8H&>8FH z3=D#oGq+q>pS_Af-)W~iHSO5&Gfi3t7g74I+NG4LtJb>rL!43d-_!c(}GyH76jv14e;w%ufv%c-#orpHbezxNclyA@YLsPT=A^zp5^;$UbSX4 zH=c2G-{3xZy7_m5IpM5b85 zNKZ+xc=k#>b@SR)X|NIgO&PVDMws)`4EL5~Y;ykDmo8h*({hFys)6^iR9PP>=1uGh zo<}DY0Z;>tMiBV6G|Aa}U;F^)fD_ZNTsm~jS4Pn2DWi6^rQNb+^I}sOXIlUBCy$>* zQyfve^Qnr0BOpbz9*Do6i>$6eUnrVq=(hGSMQd_W(OY<8OOP?9deG~(Q`r-PSsq3- z=5RVJloDfS&UrI}R}6SbkQavll%<^L;DObzO>ISZ>~*5}m#q%szwLD9uHAFe5;za>LnpO5u4O)F$H>gUnhOR6uvF28TB zMAXX-67hNVEGJ<(>-HK%A&Yz>h_R}57NP@Zgo7^cS&l^_rEFYa2 zss#M=sBG-8zhnmwg2XlRwv$Is5rB&w(A?L#k$|L6pFR_1G8Wq5t{>Gnh$08hv(L^) zo?9;m?w$g69~q52oye53%}N$@O7Sw}n(}y!akP8O`O)6@-1+mhP(1^4>eQ*)NQ%@?9RKzcW0RHW?|=Rg*RG_@+K4_uvfW6( zaHcM&=<9vdN5823uYdhlZ8e1<7ENJ@X0r)t zRXLLk7NXs9P9OvU%CfUVB4N~b+waecZfu6Ta47Qqn$7;pufK+_IG(O&P=r2up4ML~ z#Q8)dRwk(AcYgkTplR9+_qv6xJz)Ky1J;aAh*8rc1%hC5-g-#Qk=%U`O6&dvH(R>c z$7EFuI^}lu{JDV8sxyV_h0;^5B3E^&jcHN67Ru8A21X6M#}V1GOthKXDz1f6qN1_K z-C(2>2OimLCVBHvlMUf`EkyS`O;I%8$s1HkA+wYzI&h{ah4H7@M$(@cx4DL&Lq`!h z$Wjt=%1fq}#c2Yfts=S68`eS(}nAJs8wpue1q__qv~ug<6c%{NNy%(av$v zAZKy_IS#~gbMDolTQ8_eu-|)V(47gqiUY|(+cvxHBc!u%@*gKh^-ArB@4sI&WtdCJ z`Q+%*k6RP^@%GKz#Rfjzy0^Y3N0450T4SK))AEgELQJ*v$HwQ;GVEZvK@r9qazJUf zQtO@@^ski-8RVEUn$|4lO@ikrRLFv8oMR$=;Q5H4jg6#WrxHq`1d9?7g-0i}lxK8E zmBBFL)@@IMH^HgW4@9)(XF7M~HKMo?>iiyfUY^h12naJ>o5m}xu%7!R48-x{#{-c# zd+uzsEX~Yj_d;=1r+mpz8uh3Nir4*d>d&O`Z+%?))}~-7Z`inD)jJpJ(LnZ;1Aegd zrEVGtf@$@7IXO57Sv>D5L!80gKDRv_!P*j`2!!wHlcy2Q>+YyK8t1~bFs_`!#rG^n z97Bj|D-3VEmvo8_mHE2;f9I|upAB&sRDrS~ZbB;PV`-x50FrJCVjhw#9S`xWPg~Lj zVf?mEL>uF^F4oj+5WP+Q`18-wfgkB=1w>zS=rQr<+V$wioM#s2?odzCfv5Nksvj@A zyX`95xAL6-ObXB7`t1WmCRm0=a4=#qVE_Gjwp3`-dB8NisojL(C$d z3zR2U0Azt~L4>kX%#jSZwN_iKmZ%p7=ggTi@t%!8|9Lp240(>ycb1KIkP&@x8gO@J z%ld!&7U{K79=u)Qbm(1SFZ(P{kpl<9hIZK#TLu_HwkN+*{#SBXdL@Xq&h>IEdg$JK zgMF~p#QuYRC}TEUs2J)q)>YDBUn6u%ol?$<4a9@_i*-4klrrey`|rL>QOnnHC>**x z$xI%F_ulV%&d*KWg;P~PzZC6q#25!MOMWsG0%wQu&XKD8T&_t6<%#ZXaLO0#sy(XQ zJVVC7sl%rD{F}1n0E+HK^U^S!4d2U+S_Vm^CB8rTU0Q>gaAFE3^rj^bt{kB}vhqeo z=(pd03ldeU!ii%1qQ1J1r1TzDqU9`5B}PAgKPouVzUPc}LpUx~6o5c1JRi{eS`beCWDkkS2zYPP znSaz|pJ!t13`cF;otpvv*OHk?z0&VnC!Nr$VvT;9`7w-*)J4IPE)a=EYsBfL+@6(&D0l2z zO_@s$7)2`@&9J-sotW4X&pZ!BA)GQC3dV0&9Vddre>bI7%p0#4uZ^A#l{5{JwnH+^ zPJ!wQ`a*R|HTw2^kddm)b4+zSgl<&V#Awa*8SWuhGSFf60HorJgNN322R6EZ?k*6t z@#tK*xo-b@YKN2aE=5Ih@D@6#L}wUs&e_JwS{aXR=u;*n3li@neWO>%k@S9p+9{eNnG5^(#01N$Q{Lm^XA9vG6G+?LHRm&oIqnwpHC^R`83QI7NK zWe{7Io2E3B&PBiV`(-CQ`9O?maBedqk7-PKbBeY-yZcFzOPwG+f{&5l7!|rn+Jq*Ou)KqOy#dXV_7xnkgqFbI+0Ymdi8D818 z-S?mzdrI;a9s^Xua}kLYJ@*So3bQ>&C)PoU*J{@dGnB5Cv1EAj^{8YqcDSC*$S8Y` z9DDZys+Mb(|ITyE>m{0>q@ObaRR;#&CeL8VLizubkie+_uk?(NzkgM-yNPF|UOo>q zSxOk27=xSkezW-po}p+grv{=VH$8JHMo??ju~|=IU;cUORNU*%okfdd`FJxN>WAOh zE7B~;#Rz%1ue|5_^N&9Tl2T1i_&G!mlIyv7&KxGF)M#2$sV*tGLFOwJ(0gy3xxTrG z4uD)}Zc5^?gV+n0usHAzGOr8cF^}-95+y*mOZ874%F4xRbEd$8`}XXO1LavT9AjWf zO#P&wJvW{)rG~*QN~BnGgg8>x>8UF9a|T#6?hxkL>!NRpvstGuhj*kja0Mx5BpeI} z-_$aDVXZ_tTS`d%X}Wh#3b<|J=0uTW&qG`4mcaKoR+Js2C+FJN{# zVu;0{jHrc7h@==u3YLhP88K;+suSk=?K2Uw=j`5g?cBMnQPrU4%C(|B?&5nb{_kVa zY^Ra#sBgl9f55S5<|Ibh(TyA7TnqB`Tcc6Sn}+3=Oo`tHbQw+DV?_7#JwQ_ z9;lQS3J6a9_17mQ$ORetZJ>CgrC!PP=#9{~XfiZfq%F)ts7B)^7b+|EeuLWjxYyD@ zgT9bUM@EW=worK4zG6s1`f?!rOg?Zik5+d$M@K);ncDpnh=^KA@FOyp=yLn^*-%`d z-TC=@}{wmn|ad?_`7#S9+qK)&|t-a ztX-o>$91hGfl^EzoeC)Oc7h32d~DcoHCX<*-^k-_AgE!B0gUhE^kQ=NgH2Lx58-*FD?!D_CaJdcir zAAg?Tx3^zXGtA?7S4;&s(0@e1iAGj z*2r3%JaJ<2dvyBkTelnbTR=pVo}urvGnz{m^)irRv%hnYKqm+^q-*UjrI7v|pOMlt zN}NAeV@~>J&^-uc3Kl-{BqJZDmF?ZPH|Fq)V&utRJbw|yxM(X{DU*Ns`c-&gL+QOx zIyetnMN)_qCU|VACI~S>D@Brwk4H<{YD^UvOc#rVtFuu;~tTH`#3G7@cGcZUO3 z%~Kau4kOW%V7d9`tq5d7H@Yt-ik=A@gNHL;dz+ljYTh*(KP}W2qvg;u$q{HoO5%K4 zZ{*FYND0zOyAFK1_R=bx01W2YO0M2#)70Cexv?6J!48uly3r*yfEXA2qzi? zp)>Lvh(-qibPM97Oc}u_rAdcgZR%R+xVN0hDT>bBKc0aaY0E&tArj#+$o0xA?(3jE z>1s;BB)=ce*R#&gH%6S^x%-L1>_qH0Fl#P-i=OWZ;=hy}q{C?sj+J}W_c@oiJ1S-_ zUbq<7*NATb62B2S{I?%5?wx_$pMm6_0XgjhiQt&c)|I~5r%DQQ`KN0Vje4%@O#%|H z6+|`q`tsE)i$koW?igK;YouLL?i(pOJ9c(QM({|Uci}m3;OP<#nmA*O|3ryc`OFF+ z(-(>Y4Qpdxn9WZ2cz)15Uz`I00sXNtLHL<}(=Uw>X_w?c z=7vR$I4c@rJt^DR;*l`=bR*u+hS3c@^*s8@a#wn!)g5Al!wy=!oNibbu(8t1b>JxX zcgcHIu3&>WG}=FcD2Jo`R!=;OjU@vZ6uzmF_wqcSK6$c8`Vm!o&c$r>J3)r>d*PS` zYjq?=?DvbDa{8U8&z{!qrJSO_H*(q>+GcFxRwW2H0p-0&3gk(GJRb*q=T8U#WrF0V zwthqTS^ah%vf<(I&IJ4WX+(<=g_Ml$wdB+WOFun-jPz=n!1pG zY)g@G9_k_L6Db$9%6B8L3jt9bpp0s{UhuiPK&l|GHzvNOSJ?@%SF1k<^K-@;H4p;- ztDE;b9GoZVbqw2DbJAK7PCg`VciCBzLF33vZ{RZcxwD*8bkcf^ptOauPRJj9GM}Jq z)g8|gisvl&oy@al`p$I%FJ6N_lfx zE1W+EWTw9|qpsT-(wW+6-t}tMc8f|Lr1b>xy-jPjEv?V)-Mc;^bPcL#qJaSR(~m!X zRB|fH;=p_E47QEZ1`{Z&J5-K+gD#?@BL5wa z=|RuH$qDOFC$eAfh$Eyvtt4cwiqy!ri`WHpqICg*wS|@_ zZ_e<#^QwexLqbLl9qMf=Ha|az)9e!61nrMNBTMej`{c|_RgfT#3&T#gr~>G=L)RCB z?xAP`L77GeC6l~TuEFTRrAkwZ!3-A@w7YSbJ6{8W#PT>ryLku)XeyTbO$7x!`h;r&==O1!>SxmA(EQ@1|0jFI#?BBp9S zypbpgym0A4!1hIsv}okBIl<=jO>po-?CGNosy6a|2hnpD5qk~r>2--AM=&>i>O~Wi z<%H3~{D+1hL?b3TkJdxl15#pb>m@qGB}7MwbJ1vKB<0ZWMI?_vS4BYFpXY9G?d~C` z25QXd*ttX(#;m(?p9PLcvvbLjRRQAu?89z7pPn1^ck@M)0S1|8lbMN>cvkQ>IHhgl z`FV^_P2Mo5(T(N~PYi%fF>EX|xKgzDEVvJHjy83kgYB7uyKP__nW7h+fzWv6a%_k& z+bVtjMP2gt`CyMn3)Qj3@<&QbJ}bt#rtjM$a`ampG{QkbG8N9}WD<)KrCjey!HOJ4 z1KJylbU=>G?Nts6T`dY+?QT@rxZg?$N8~EM__8*r;#!p`GHU<&`Cm=fj&g>^X^zuL ze*xr8iZYML>E5U5PB z1HEzzdud$lQ%GpRTiH^aGw4*hZs$xMx?gEaP8_4o&~A@$(}muK*w6#ko)e#+S9R^I zmvO^dC|hTf3{^R~KYP7e-Ux*gJ^%RQ4~y*jZVEj_k2P{Ws=}$0Qn9`qZUo}xO6cf6 z&x`$QMhtF8dT!8pRSpCth|#QfyQy43NEs=-cgnocl&NgMqxncM2#Yz=77SArT(SP> zQEOiifIMG_jOSW(d)r@j`+F6KV|P+4HoN?qwvC=81A0B0cDx8U*`ckDoTZe!U*0P@ zr`8O%%)@1nkthNVK{2AWG0atMa1+ehlqNvToGx2-lETrv5fERLLJg%QVe}|y5zEmd z&Bt*%-xOmf40z9v5z*sdxhP%Q$NrgXGTN$5_h2w=hX`80 ztGza)oZsu$Z$t%&G*c#-azq(TDH}Wj6)3%q*mXr@WPL6)Hk#<9&*bpqtr%it%|x`) zTJ>kiB*y#q?$>_)>8CJ`CO#VSq@fIc&VMDxu)5~w&Yur{LoWssK^fSarPeI;TyoqF zrhRKq&>aGqRs~c~5Bi*y1PeW7xl@Wf*%K#D#=&6BL90BGqI29>9Pk4v%EFSScuB6& zo!#~+*@0ny{_J@`Us%>fpWh)-ZjeMIB68FKMYK)9F!GG)M2w!@A4*#(mbRGo1BEY| zQH}<(e-VoPekezK0FlJVRQd0m_kJJH+}kLJ^j@3=&|cJprpOx+X-iTTG4ZMe&$s#- z%2za%VR!q@zjKodxjWF>AWi=pWPzAOl}mc2xi>*0!GOP(*1FnVtyPKI_q6xyo8^=# zLyl@~=uG{;=sv#w7CDZ6DfDRYjdeGQ`R2b#mVV7Rf$oiR=^CzhVwP@)jd zA47ZL(#1s%Q?JDN_Wrs=PEHj;85_VE{-)B-w^2U@=h!-m-Z|?c`;3t2U4cAXUlrrI zpGk(d+Xg)*<|Xtc??w8+bGF_Y+0$3SzoozE9?slMo0B;05y1y%XC>*gp0eCQA(Sq- zMPy@>@ys+Du=(6TX1q_NgN%*p?_idQz8f_gN&gkOjGdwEd24gCbDy+uT+gxysg&&z z2a#CA;|6WV0G7$3<*VmSp;M9=$&@!msp}c`YwU%TH*+9VIf&9dXMZA^_!)RzN=9j1 z)W9dtJ^E>e*(^ZPNQvE|gL3!A?=@R#lMe24j5d zpo?cz7`w4034Ij0^V~@;-Ls+-mNV4O%!?NFbUiq}3?1}F&P3?X5OdShr$GT;!VH>IJ$&UP*to+|OU+k!8kzk<)lgNc+|*$^g| zA~(AigCa2@(Z6r$cUqAg^yPMz^ za68X8FOG5t3uMvWhevlai~^zXwHnR+;N^q7=KRI;F|F-$5D#DUi-$n*G3qv(O)er> zsddx}nC;P+Bzgb0)1D=afZptKzUpCz z69AVN`+;K?T?5N*fsKShe*5-atrLlU7Ri~TGnT6Y5FIieP@Duvczg=wWNHG{%QQvZLJ&KZtGiOdG zhi5!WXQn_>WnNmO`}f&@!e}D<3`+BrJpNF$AfgR&jtDv_#(dcGKn{-DrcI+^R38B7 zKo`G0c(}-P<)k<;UiT>F(_X(WjB<6}HmH4ZT3M-*N6JYZ3^YZ&qeb}B9<`o4a%;Q@ zwWc>@1d3@{8{)mJwcg}|wB~~<*2n1EGk>3n{@u-3=E#dJMUPF|gL%$!s>>O4hJu4w z9dX+13CiD!f_0y}pRPSOLE)sF{Px7EI?~Tl3Nn{J(W76cNd13)`A_7T{ag1srDB}g zg<@jTM`x$o5@$Jm-iG9?JF|I&u5-Jw9FYyZolXX>%5_jBlZ`ovHqzxRv*H|IiwGPqb~DK2K{eP2gwt%%uID-b%n4 z`lTELyIkePa?(?Sp63uNCg43?-*h9Osr|`I^Puk}4{}dAiHWfiZDvDl0p%;@Ox&mq z;z5bpZX~Z5yhNf|e=_<=F6-H#+X!-g(@9D3w5!pLO)Z~f(<=`w7?Xq5CX{#T%X1dl zIIIXV46zPdo0G$bEV(Hu*_X*PA4vy4-y=oDAz+x@n|xY6SkXJW9Ji<;g-LLu)Kx_ zAc;~^&!un+WvT&5F7oRw zwuD{C|6lkxz-#ooh;^Vaib+3&9EFR_t$Bw0>|=S`^={CWpkh&jQ#}WfV7u_9y=!QT z^%Uji=q-!=Y<-&%;&Ro5D1Lgf=qf1Mm$Q_+&U=e~5T$>!gh8j5^nusNXbyT)=3avc znVf+uN$+F^`^^*?(pmUGYTdVN9vt38zQ)|5nFFi|mvjPziy;xsnoTanMgMMVleE>2 z)tg`isJ&2dII)()jSaWf~Y=)=Ejv5tEw(X^^QWs^HucFV!?E-{|C1Af-@^iS$Jhgj^Jb&BbAsD<){!DaYIn=rWF~p> zh~y{1^0}`v0jD;Ci#DVO>HC8+54zi;yu(A!#vFoSedbH16&V-xqZ)QK(KIWiswHT2D%wrKG&j0`5~vIat{%q zr|&&Y2Fd3UwbcD6J?liAdHTRJaSom*=SFu!x-uqMrhBD_1x6aM(6uLye_QL7I8#u! zmoe`6Ms(#PDp(+N%b_N%2&Xd7qt8;HecdGp=Yc-@SF}Sw&OI?yYWboYpm?FHQgB)j z7bilecsAQ+y3_Ts=fhdDwlNzcizUO+BD!H)A#?5)d`lw+n z*xJ1#y>vqyW-Q#p6=myt7{kHduN59Wwz=o(`;;$5K*5f^4HkDl``5JN#Dk&S`cuh+}5JECGT1Oobu+$>0;!8D?0&2_`OWB@F00* z^&a}0vJ`2(!tG;aa|N3{J2(`ZK_;9dYgP5~KwV0y23Z{Ts$(XfWM5>6itu8=vd)gE z12N7&#>*V5z4A2Jqt|PfFTX8EViq;))uAhS2vBsHn@0{R+QnMCV8qLjeJJjaDguhf zKI358dyGag@omFm1c89354jxM{QUYvhq44iJ)PZ4mo7!)7P;y#SI}vN~x#}>3W8vk!o3}G%nc{IrgeS&%F_H`&`|}I15pVc;smG zNZ7CjpQX|?5g?8UWQ&0#Q7Mvgw>jYj;-EAm!$f}c(#43H%wm6*4zFBbbsrSb{ev*@A`;qN9*ff87%=uk zZjUnn)xf)G6k!&J@=mNDw(X@WmlpSy?{ABiwBw?`j1M7}f+V!eqKJo3x`C+;8%yl62#fului-~$E7whb( zwwazD0LFS3WuZ)ntkxY#v&tI>5jHtzn%d+P2WNxh5v1OPr06Dx(T?|!=g?;-oI{;c zZ}k8Ex+vUp$J0`kQqRfgg-B=IjR8{6Vo%W#QAv=3g>mfsq{sT@IMvJQd};#;!dz0) z4x5rIU737+Xs_42U$+RYmn4rt5NiSMl{=TFpm=zUzOqF9V@h3E%T}18t0kV9TwnbC z-P?$mr>3VOlFIqDsuVE$p%m5Mia#Mc)-{0hcn$$gw*0XfwTK z%c`e+2PQ%%43g$34q7W+jJ#e68)INcS+PN-~ujpWG6(5*LQ%(xmLit{m6vBJG zbm?*!9uZ0}+d8gPK&(L$NyfY2Ol*k;5{xk@rg;$&;rslDS^;h|Sz#j6w0ft2yKTa}nj7kgS{r zV+ft8ZlyFh_V@2U2y*ahjEEgH87K`z@hUsfd?p6Kmyu{AaWLiDw@z;DsWF!a0{!za z4I9~+j7~RMiR}mv@Q3fe55xV(AAcqXV?)s82bbJ4%Ai>lw7`iIi@Auj}fpRBGGEzR<7Kh zGzkifk6A1gG&JU&31q`_m129f|J9!(iLwVOjhYl{bjvh+yMY*RTT3XHGMIc3k#Su$6^qRJGQW?TYLr{C39K!FD zP8>edLIx^A&k{3-lOcWZ=waDmY+G&9_@*!d4(X$Z4{Hw}KMJ^xr`;>4Q@)f}&&cp7 z!8)2%FzD5igw+s-18m3;Mr+fFt!t)a3TaR7pCXq+aJV(R0HdazfuZm(pPZ7!LTR0Ve?WQwGwYX9=QHGV1DIqitb1dszEO-gd8} zqSI&2)V}`u>yO|e`OYm&QW{EYayq5XKYsmRtc&)gaYTpGck8vBb*&16uAhkKB>hIBEG#%NQwf2Y44v!QGT@wT`oJh| zL^OIrPmYyLC0nmD&<1_A#f*0O=&I5XHGqtF?;1=7`C1W1GWRzYk#pX1NuFU6(k=at zs61QU=JHSvM*jNs>p`kQ+<6~mb+hZXdCY3cgL1eKl3KNShX_s?;r1owG%)1IvFYbb*+3^p{*fEhfX7>S$=IDIfu&SZ#I%bP#6xBgsaCUhc0gT91_A}R087-x9Y z!s}W$%$crSuv5X+40)~YEW_2hZWN6Tr5MCmJ9wHP5 zrJ7YR@gNR8S&L%h1sN*kqj}*VR;6ge+2Wt4FwjvkaOjJG%d;R!g3d)~_6WGv_oQnW za(UFPL)2s_Lm3+MR)k?!3Wc~e%$W{lD7_QSNRSZq?FXG>r-x&m3_b1)Xq{14S+INe zuAy^1l%Z5)XwZi;l%WiaNOSyo>Qq#eyll%e!WhBU&V3}q+- zB|~FAl%aGcLxVn)p$ugxEePabC_@=!85;DV3}q-oX+bVuxe|5q{Spqu(1{pIJ2LE! zGL)gzNuI-OE^{=QF>`yg80ENW)5hBP_~w#!11V?Yr7P=g-F8GGbKC{o;7owExXFc=+%^?bhwvF*|%Ih|#NPcp1YqTS^ z*M=omD-%=MGoOoA0e2zFSlF?4NTN&e!H4fLYP^t?TFGM-tf&KeypQ|`$qa&j+JnP-Nw0`H#*M9urhtED(`z#E)=w(+&kv_ zJ8WZ%e)8L9VglP2hYp3JIFOV$MN%~&zVAAHZJYiOAs8?bV_=gJx^(4oL|_{?Zb-j7 z9nh&ZrbB5@-o(h<Thm?{8|x1Z=w))&zAvY~aUyiAn>xC3gHAUw&?0U# z-H9_|s+gfO1_F)Si_?Wu=xls*?32u| z&s+E`4EpObViNsbx^ywYs6W*1+__sDN`K|*)vGbJ?B&Z>(XV|XLDZBNXwfi^!-o&Y zjPWc6r94hwC%!#VTMaRf{ZdSR+aJ;O&g4{_P6nP~8A;K>#iWQcXK7Kf`=|2Wuw=?5_|2lm-p$*%UqwvKrYHvdrvuBqt*Ty5gu{9wkmup`iIa=$N-6WF^zXfU_bj$54PsOJz0I4(Yu9dEkD(%CwV^?8 zRX9qb)BE@Dg~2;^>{!gE?}uzk#)4BON**r<2|-|1GD??2aQpV17|~3m5&r(?pBPSa zFB$s%`}P&R>N$VZKo_ufDWMf%kjU)HW%y%IT5xiHbudKL59M+WXG)MAGG;}mI_alLQvz96+2 zu)*P)+c8&rl%k&d_wOg8xh+y8Uw-k$s`4Bd^If}l2l3fe-6_b&D0+ zsd)J7bpts3my;s6mkhe-*Ia42=Rvz)Xo-k|lCUqXT)7$w(q+m^Gkl0G`}Pe6gBQ$h zJwfD@EZLuO>Zebii6~u^K&-Q8Aj%3j`0KADsw4h=;ljm$=D$Dr-LRx>4GP1iMm)>b ziLJ>YeOgA1^D%gxj0&{dGXvpJN(m#!Feo6&}9q^x+EnsHEO zq8EA7p>$s+w@!vPjkv?OQ$SI7KcJp9L#~wTb~1$I>GNj^RX88}WYAI~oD9uAkoIy; z+*%{@{r7+Vr}qE;-~TVt2lC4AhuP>w@*aP11`^Hs%wgWT!8s*&&H zJ@fAegFo#d!YMp-=uklKrxTKOft+dQCkq-JqnPiuMa{=FdgG4kJ~NL)ub zd%2nupHGVZ%9SheTFTJ#7|Pm~B07Gv*s(ApFo7{y*-zslMw$-gkRPq< zlkw=`qxh^GIN5u>*jwJepXGo<>7KL|3f;MLClv6tYu7^o|N8rH@pUtKkVq4E%4n;Z zfG#7yn{qR9Mnmboe39U4k)hgY%ID#O2aygLM3J9XmS*UMn(v%L97KT{a7gX-`S(SX z&Zvhnt{8KTa{fxtJICteiIYQPz6M0h?RhOH_5h$9`lYeN}ImX;%Q^?nyFT@3g<6mNoLbZc@D7`Bqo^D4aCd_aUEt937tROy^V=iG2QFVO{leKBPdQRea)v2@LbMKLNPqb5`z4z!c-&c{qm-3(D%Rg~(4Yu`71!v1ib7dv#> z{|Iu7r9HcU0DEjB^g$Q)Bt*gfc=6(SG->HbD$OPsH1BPHNu6-erptyr{Sxc%p7dYf zOtrhmm4T_!u{KH(n~9wAWOQpQdJ>%a>r@!tPI`#bdH6)gXSt=T*3|iZ%$(3}c4TxU zNSJv&-K*v@81hGwfgj3HsxtQPfB*acEcYV0ahpg!u#rb-6Y?&>rXoWT%(W6HLcG{itl{PXc)&`~^hSIu-91ymqr)MIfR3m-w-rYbyhHJSpp?nQu z(QQ!MO;fo3IeRt?$~VW3#c+*Ir5Mq9H_F?(9o;iVu1|e@a4nJ%c1AlgydwqV>)di; zu&y_5+*Es$B3*f1a}v~TEA8sFfUNa37#$l;&epTqt#mztfOhT+H{!+f7tx(?!-kDv zv>OTZQQKe7opA1yvf)&;^LdQ9vQ3UQr%J^Kr^vcRCpv?C();<-Si65plvaKj9V3ew1Y_w;_d1|zNo#!AP%oBjes96{?%f-tIbTcFojY{n zX#;DVFqOP|B#aER`*UCNf=APq#q`{ja_^fy!+ymp5xKQc+DZAyqjSchK}3S_hJ?hG ziCzmA6D$g~T1W@gKF0;mM6(Ea2`&U{p%@%yD5Kas1Ss#QzNOOKs4=a zK^CO7I#qBCi~bTyB%RypeN&)}`uX!0(%LM<+3ltHED^JH!qLG9cb~0JU{h8o6=6&+ zJtwWmpf^ezTDNK~ItbqVwmaGvnzKEnEjd=MTj#*nuU?09qV^j>&YqGQ(VM~dpCx_z zxSWyhcYgo!0Q5<~FEy@vV zg>6Kn6i_)gz0$!g?Es#nNc!T%OR>?G8wTJt7SuxKwuWGK?^E?q;=PhbY-&!U_XL6I zX{;01{sCz;(l4<&bSTRmq1u({>8T(w>UVwq{P}RS{9S1nSaH~?taGtfHI8`|B}WU3 zjOTVGPt^j2KY98j2y|cX-V=pI*Jx@ zi&8wAM%5FUC}_`uvHjVrIn|JV26kd%OBnIZWpvxleVNwgdFJz={F6d%bUqk@*A+6a z&kEgEGSgcTM)~#YH$kvun3Xlorr<`!hwGp-AEajIl$4R!3u`{>DkJ0>wQ;*2(X z#WCY#*&{yRUd6NVIvof1C7tZG-F#lHxpe6R=Tn3Ly3ksbp>Nv0o#aK zc*PEmk%UV*2#o$pn53OZJb-lAEOWk(e)gyjrI`#bX!luJ-`s9ArS+}@@AqOnX|3o- zt%1wm2`ZHXu0D^Uv@a`}?gYN&u-#9}*mF3RBK(;$9fIWy&UNlQ`y6?Rh8guv45agy zYn?^STRUvy$#8`jZleCg2>XO!2&3zkho^N=|?OX!0mAVoP*ERsMDR&64rSC-hGSTXVkUB#(Pku75sxPERl6L z%ic7+%rR?(0660st}*n8iV`&|~ zT!O6ANQ!o3tVL8@LyHbvX^=wCH zc6uoIc~u~lmo8l{oU#?_bcPv! z#xttAHM%C+U0^+kqkj_2ijy$Q$De_3$;a0pJc@cig$k|&t+}|!|%19Ttoh{{8pg!@vdc zb<0HDll*;847&dR=|4t&*9ypp+9X4>IPY{;u;!CqtB;V1DQjbk5W6!HwdpMybj}#4 z-0wJJ3;A#^)WWf5z!8D-G&R?lCjnV+ z5X5z72dUS2{zu8khSS;vQne4?r)Ry=7UXy>N>uH;uPW6ERe|)-+j)qO`}gIOqvK?G z4fHZXB=KHy?7#l@Yl`|G_r{>-i)~clCXxlIA4pKHk~Q$Ik$xEO)i#(_;bDR*yKiIqkk~|V7tUV@SO_T)j3ci)KEA2twTL=&EqOd{N+H&t*BwNy1f$D4tKtxCRriv!Kmp6A+i#JE zYM}Tqv8NiquKg55tm=d>d4A-hC&{=J=4oMZ-@Gpy@Qe)jbVClwo0Qk4%bXL2T9jUI z(4)O|`dQV0kH?(!w=I26VaVqfCq70quH?X}FBfqaQwQHI?hSvhmhIBBQVe@b$lmMe z1%?IbHUFMj;c~_H+iDZ#OyC4@bA;FrI!r*}+Imsm2sw!qV-r;^0NqmNwmTL1@{4KR zMb%pL*4qVxY&h?NC40(i5c2GmoC3Xk`AY54<4225P9E;$8FK&_{7&?5OLvu_1Z0hR zkD*|;?+XSsi1=*UTsGX^W#;Gd@|R;9^8P8ssXno?3or6x!>7(vRMao%KX)Y4iO*qx zid;(%S^nprb7#Zw<{aEo!h2UvRJ7yW)bQYV4}GD1<;dLTGDHW=|8*B&J7pWUZ{3Or zC~||7>z`pCrJKn)yqO|x(JCiQ6jk3MloAKOgQN=wc>j_LPEcNPw0FS8Sy|q_d#^m_gN^opx}kHKQv$7e)ufZ9`+>4#VVqhWqZ+71dOcWOL@szh zH2&?6-$Tc~dhxQheQx_Akr)(PO}lXTGMUZ{1hBG*?*IPJf0wC{f~&wGVfY0Gv#Xd= zvrhJahD)6&)qWY+qcig2xfX`OM~;qxi)jYQ+>`s7i7`P_(=8d zrWICXyFZ z4tOPq6Z1`f*Jk3mgWRAC>d}{z9GPSz|LWzd@E}(rJXP0W0^g3bFJiRlI&b+aSLQ%# zltf8ELF#+dQ`XK>!Ni>j#{68RcACPzS(aaA$r0RY_qta$r&gOIk_D;jryqZc$%+{s zwm&(BF+Y4{up@Lv`gvuo>AGrpT2;n_G5rCdfOLthT7_^KFL`dS_q$(zeKhum<}v#c zT#na5R6QVCap1Es48^%0sXe^^Aka&~Nv@7mB{MVA;iRml;57&)#wuxw*9qN$GD#I! zi(-J$>8?%TG$+KF`X?E60jLH&^f_nPG(^=ua~(D-vnPs5k=bG*WP?<)bceHw_rMNT zjkyv_kv%61sWc`|ZrZd6#Y1PIcXTn^qcpbNb!jO=_W|;^*rW_gqYg#78e#c0>XMZS zS=IMjF25uHkO_7D5(|x#a5`-;^`BrHsKaVBW3gFGSW*`|J~9$y9r?AR$*A8=Fdg!d z^=5plU8K}OGNNWoM4ys;*Jiv7WTBrznC7{m`{c6F5uEb zqKYbeG8}Zlp@BO~HJ@VCIrN@2j*}FYG+nE$v0Gk2%2+2R8;zH4`cxJ-YZGGn)r%VxJt_Hl+&pfWf%WGPVh`f;m{GOmq^Rx??Xf()%2vcfTML=5UhBC!p zcvXx|#hjHF)U?Ou6A82{oEUJTvbcGa+NVdoG7zD|UbQpDgBIFVo7mf#G2T~kQ z&LpR1Es!g7opP4(vSEz#RF5QXN)ac8n~nX}t&;`uuMO6;0V`ltMx8DV6f*rT(xXm+ zc^`2Q=hNOXT6ijb4(Iy#v2TKpp{$AViu&ef≤6ECqj|J5g_^vS-ikh>|%xbQCtY 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 literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/img/footer.png b/adaptive_hockey_federation/staticfiles/img/footer.png new file mode 100644 index 0000000000000000000000000000000000000000..7cd3e4135cd1e999df89df085064e564ff93b87f GIT binary patch literal 51862 zcmV)bK&iipP)9`X%0hGF{Mvr+Tqu_$@8ft)zt&_VoAU~? z&2MX(O@jm)i`CdC)dNGOJSEYrNl!NW1^8P^nU~CNOO&LZ3+ zKz}O0m++*33cceKW}obRfZ)v77sqSS@jY#Mudl(_m<^pk%HpMk}cU{w3L`=^Wl}nW=!s|2dbMb z0Vk}qbEH5S9~rJ^o3pk)BUSR91oKpLrx~1>KgoiyuvN}&-`J$Nw!A=N$hP4z6KV!v zcH@d-^M|@tiLv|5xJAC~VAkCP{V< zX)+WnBk=E$>OH9LGO6T~9P;0N0b)W0$^h(0N`AG!!L%O-)ngKNKqL|?s^WDL+$^Qc zOJ;`=w4b^xU8+?j3cTJS)e9Jlp;ECTk>@P@Rg#FzxM*|3@**?l1jvtPMrE*!V#Cz} zB>rG`weAC}>|GKxerZFcfZ|D}V|d(b>+X|kf3Ini#4s;js&VCc66hY5L~hsw10Q5B z?d=;>1_a>Q9x1+rTHt*=S)?TEFwx?qQXU@}VPfSgDk5BimQbu3FWLN!%J>zfxn@~H zjJbVpz3K3h+(gNK_}J^3^1FQ;YbDa|B$7W4HL)z=f)08&Rdrhq7F(y2nJTC_>fS_3AyVoDOqM7Z){g`fEC^aM$?*% z6!UTMpP7>94~oCsEg;eo@xXtJG(j*HFhwrO>njVTVm@Skb)Y@uDwr=w#ivuiqYDai zOuAgYTLn})9%#C>xLi_{YaXm=G2Qa{FxqbbUSLnO2_ikY2pJ2oEFKnw`r7hBbDAXh zBwI_g>Ls(%mdP36GUuN^*;8v;heiX=HuBZR4<9y9NPq!Tf$XD+%!l+cCRFcH3A4uI14Kf133{@*Q(2D` zW1}M?bieR-6w{DpK%QY^MygpS-wQ+5Y6GnL&A}#fUSYPfCI8&e9#ZW;OSWV)89w*D za*xJEhMOPnt_?{)nG_wVAjhtrLA__W1J-3En`h+QTPaV_JUk+?Mw1c?ddDZV{WTN} zV0wJ&h-Jk&+V<9_rzi*qGxJLMylc`@OhR;o85mzGfca}d0g%dNQ(P{W4VpD(t7pdi!)bG+ZhTCj4WpIo%#i$q(fO3r!HzENe$d|`csd8EG0 zyl!Ee)jQQl)IJ~uko&5evJAMrwX%k| z5sO_{SzvaGKfy=O7HgtI#JU4GeR^Gm6b7TUumJ!W=JCCeQ4!{hWf`^rNZ6 zL^5`*fM35p(741zTC$KffcO)#Ql(gvXui9nI%Gl51c?XY1Q>1~b+neD1;iaeE0>ja zSnh^5yZYrGiPpI0hZ1{)0;9MfG2V2_XSX8nX3Z}v$ulQrr7SP-$C=!? zI02BX(~5hiSik|Kjag}?OfJ6%q#C{$gsV72_Z)20Udi{D=Sx+n^=LJCF&UI++6C!f zr$^ppRI6xp`LAsUB!G!DH?1hv&v&;Ru*GNu-4E5ZnD>_Ds~Fx=MSRH?V(}7uUsSlv z_(E_0)6E^GZg9w4R-9{!lM>9s^{q>Z{ydEqvJb5+R>tG@E^7lh zK!i;a;QZ_BdrkK&8b_?@gEdX&D;rms_Y~!*n02snHY%ZjsvlZeVlJ18?n5=LOKnxN z7;KU9CwG3f zwaert#+naF9{9`s4Q4pVXIMVE6jaQDjJpussPhW4l%@OgJ+&%70c#rL&McNZ*ver8 z?-UC+0WvN*f|>L49g6RZN%fpc9&H1|5_DZHAW~HL+!uBz8Si$bR5*(g;*?_S4uH`o z&);*%o@#;)-K*8A3<+rFV^qXoYti0uN=~|pTWeJ;EHKZEu&YUueAr_I&WmlWtTvuI z4l+VpB*L6+5bx|~;;X?jxuLSiB>SbDNm>C9^c~XVfTGoWTVnz40EcL4Ls0+m z#tw7m{(AEpDS}nY`9DqW_Rp@b(BID;zjPM_-x4Do0~v^kU+d`esr7y~@ZTN0<+b?U zXz^Q!1>0%--oLhY>*u(>vOtTLOSWX8W~p)x@PE&=bcUp8w7W$+TbE(_;;V^8d!(>5 zVH2AafALs-yXI_&@J9sserOyT(Ka^nN`H)Z%@C5(lmzqHb(Na8qjU7@{f!D9rAu+; zgq|XjzBetWVbZE~zZh=6UnV60I`vXG=D> z5y+jJpREHH(7>`IhC`!{_9b6ln>z{LedF>%Wt#vR1k;)&*!zNr81##MX)K?|TzH5*(38M|6>p;Lc_^sz9*1Nnk z&s)iGNIZYgSziGp z6@IKNG-pddN>D>Z^+5lif<@>%(EfLmH~I3yhGU*!Gso~Y6|YXM>ZI zdj6{WhxLL*OmF-hm{{;UKC-IBywTY!pl5eTYli>|a!IbsNHr&Ar|M{R*o3efxcL9t z)~##lzW)IcAE<7UD%>)2i+oO6bZPxuve^yoK3I)E6Y$8c`-wo&pGl;31H&dMD#~^? zS$;yCY8>Up#|b#!WnO9T_Ucm)_B-|*--{^cp%A^t17U#no23xLv*5YhwZC400$=rE zn6#FpIyVtYAsm(abU^(2LWKMI-Q2%uPZE340(I-UG8Gg>%WJ8K5LkmfNSs8h;*V|+ z8fnDRhvW-TGEV%)h`i2gvX1EV@tTog;d+15Zaan0dVs3z?9V3~J0$5V)N$i5Uzcpj zLcvbVN!O`_-`P>+w;x3>i9JocJu!7eHFcuIH^W9oAD2mo0~Sy09*`J*NaFVuDM>ou z2n$5r!3AhDVfO#tJP@?}iNY-dk%&_(QI63jhJxi_Hj)4I1gE8V=-(PI|7xt<3X z;EL2_efBs|K%eb$d3B78n+{p8Q2~(rarY!TX#zgc(vX`FYu1Y+KBpj4+bMX4)$+XA z2V+ymzh({-kFJxmuv?NTd{!ufNA$HBj+4u3ayPzLu1-zjyU$<0+`&a6ZgfhLGtZ(!Cc~j&A^t-Sy+x+_*`%U-AgzS$gGwJp^ zCLjnR5qf;=V z7|eaqIXrI0EzXniB20H(A$QQp0=l{D<_6lD#CQE!;s~BAnt_l4zzhI@_|#wrJp!~5 zkAN94JgLPC`YHbp+?mo<~5Zgs2GJ6rmQ(kbKn*j%ebABMDXdH^BpKgFAW# zRR3TmGU~l=!waW#--TPmpB*j+3&}mGVts4jMh1D5PUz(UiGD zC(jKm>NX_kmWXUZ4M%X3&(8ECE)|jqXqZ$a=L5L%6X<4i3u<34>$F|YDpO#(M<-0F zR9&FIpCBOIgzR&eO~>nm6|{j600od^FNZQ~4Yt`AHY_*i$=dAf?KijYtv3%zU<5FW zuceLQIY|Zq7A}>2Lv?1mR&L%ppR6NHgrekl1%P)8c)T>J$-7-xn4>`pV$pW)e*jDZ zoJSivd^*cza#zxdgfHkE9XD1R`{HvODiwVEwp4AOl)}!nQas^z?VEu~${uQR>Y%(K zd+wdYYH@(NfU>!I7JpmQqR+~gJwq|z3i0oskUMp`R9ovM5Z>84pe@+6fe%g`Hf1S} z*a+q*&wrs}*seB=Epj)Lj9jVI{#cE>mL&G=GyF2FZP-Hdofg|6>&DqHmU#O0j$UP` zc^U=U%VbU2hYbOqN(+btdv3GDlA&H`U9#ifm`*t*UJYG1e++gvx^@9e0Se5zju zTo51QqhD(8Rnh}(!GIh5S(in3G4}?b6~%Y7kLu-KMZXDA{0|DCMUDh8_J$3Ub-Blx z;`>o47;Tfha5P9mS7^be9gTRFd+Am2bzlX^qQHix=s{71ycNQN=LB&8z)j)@Z(Uoe z@eFw@fcD*jJ3Jxr%u>iZf3p=pT9=-p)h+sQLN+uu5pT_=A9RbG<*-01YEX zLvt8=B7B~=>Mhy6ejbhfPZsp80EBEz*cW$ZX=Z)1Njfsjj@=~|&&fzOjF%;(lu zIH=e=tIgAL33>}yI9DW@w5O3stW0$B%FoY^_Msl7S9WCH-v;xS9 z&h{Ssj-8;6t8(=~b+ZDfcG~#V5oLo=9U~x`&0xBmUzlaGr3wnLV|7~{!SKznjYh`J zH?|+tfSIDiH7ZCsI3s|B;XBa5gVn-xlXt>c?_BZKOs@fm{;3pHXx;Hu=ZLuI+U14j zeF7joFLxY2Px~xws3iXQd(JLJ7}zIh6mFEnoYr)!m?fffQQIFz(VC*?(sN}0D{+46zCr0P7B85J@zUSkYGu6*Z^=Z z*^>EUu=OeU!#MHho+K{(xa6y_T)r#d(MH5|dN-x1+6|oUKT5^$dA_+-JeK#rd7Ye&|m51GW<%Qix&w zz!GS~Cs(AfgN7x=ttISeVP+eE6fJ>0>?3-+-4ETF5|o0}-Ye+eR@wC40?R2UJB0yS zur3bEx9kqV3I>5S&SK2T-Uu~g3L5P!zEpokCLMsld&SA3y}(Fem|%!&14IGrxeIVZ zqx{^u3iCo+m-%B|+cE11!|XKq-SnP%CaVN9Oh}_0jYAy~9e#YInR2vTsn{y=*q1w|F zLK89pZ>SzoIf6}jQcjvOSJ%oHtg2IF-KY@aqdc{qtT6V*h4NhRA+L1w*j}yT{pTgd z>bPEj+u4Q=aE{+AAV1XZ=PcIhI!*5AkFPFK=I9T0)u?ZXP{1l%bz2@0e>B(GZ#?8+ zA>lWMCXes-NQM`DMnK)2-TmT2TJ)~;XEi89p%eJDBnS`6oyTAUZz~Td44}lFc)8pS z?9ZJ7=2^A!+_T)zg%Ye^DzDEJoW7;@BTFq!r$9M|nk*F5p;ZIml7bvbr2AUIazcBQ zxCusTFc{bxJIG#scR`La;ok{B@{)y?0j|=;Fn)N-yx8X72oCO`-2-IBvZg-0j=0x0`LW2eNblUf|4u+U>u4vKumH40sQHrw@ zv^z65OX-1BkVw*?P?EH2z$4#t%j!~P$b4r<%~2)_>kc~pPbaR_MDGkqLRf3h$`b$x zT1!+nIpkS!v4Y=O9C*GWUWX`QfAU@#y(spNe{_!dx$#@ z*6joVXg<2KM1yx69+m!K3#x~-$DrNyh4qz^bSG;dn-LphR;DLun-dowYr{V9mWv*7 z0!f@{SStX;=p}Im4lUvY5vJr7Wx3}2Qeheh;fgy6!Hvs{RX5{@lGIc2@^uV1=Mcu; zyJU?KNAv`CNgNOK4Ve$FDAY3&Okc~s#OGWkU^XBp+IZ~ACmP$OAknGG5YwY*-$bw( zc3f_Jtb*SpD^|YuZ1GJWSYBv;Q`HoZ;ED4Go9w!ZLOpM@6;23}E);)x$G&>6J^%AH zSi`riEj6`LafIOp-8)*vC-W5KcudxTiJEhPb$pgM*|VGEPC%gfcugH)1ATa9u@=Q% zY47zL;HOydIq}J~MSOEdl}~JI?o{qu?n_vMT^6!C`0d;|t>OnhBKHu9QR`B`ZJAwE zoTHWWy%r1;;Rx>UAJR(u2~rH&CU>)I|4?{E#F~2@ok}#^kN|-Fk61rKw1Jq4J0t~} zqU1P9e#4g%w(UN;@MZOOA@wbq1lANj1lz5%MJ?O*F;+J8*P-sD(WoT;QF#{1^ zMof&{_gQi`Cu#8pZP&Hw$tqjH*?(02%&sC0aSseAx>I&^2WO%+u?ExAxM!!P9fcG$ z8RH`(RqqqB12P*|OOf>BYs!?QLiYybj$MKspvU&(-L)z?Gah2yOpDoU0g%!qDMi2) zu`>4{6J`r)>abhT-k8l!l8W31R}`t3HZ4db2%*RzL9{SSD#3B$G+IMwZ1Kv}WRolj z4BBsi^;!Nmk|LNYkqm$hTW??*iYF)YZsXi!( zJqarkoto{9lAw?@-Cxse4$qRH04!1UMI;#QmpLtL z+N_dif-?zKLo6H(io%3=bH4zr^!i%Gs7R5uxNU8@inBg03E=(JEt-s@!NBj))~@7LbyA(id}R7fqNm^D^}Aeba`(o_$|EIHaGEVH|)SOQH9| zjQi%&qFjewq(v>@oKM;&&lTTEfd@blV~+qV(*}t@$A`k|XHq8&Z`jAgP$a!}>YKRs zDzPB`>Uf)oyJVqj_-Klwjyv4HTnGcOVWU(3b(4QDqVdJqP-bZf}04Z2K+6M+6vUX(YM~X2*9FCQKf)x557)N}O3!4UQgsGY zx&e3LAXQfUF?=Ei=+BaT00v!MQK0Mx_6rQixvW8eU=jrc*=I7uSV=HoDj_yG9e_)h z$~3^wI`J#}#An?viS&F}LuY`Jd;AYu4S2Qc{HC4iU(to@dJ{tkk6Qqy=OSE|bi2~Y#0EPSg1fWPJDEKh_=NGaMtTy%)Llv0RMtk0o z=`izbOQ+AePD}Q7BgSFKffZV6#L^U6FDcH^g5lWVDU%}wKX-$f=E+Wj@y3)ud^~LP zO_^y@ov$%FuK11@9M+B@2 zhQS;nhTsblX`*$7*sfdlGNDfSzlvK zMzYzMm7@O+!panqYs(9zS`vBy5(YX8+DtNshC#56u9l*tIMtt;ufeRMr=G7=Nhr{+ zLZ_i3CBaO|L4K&FMH3wkFSO`Stu0ev;>UYx^&r!ie?@7owo1Lx*{_JXSA88h`A;`> zX)u(Z5U+qllMhS~v|vD-_xUJbPP<%#1o;TjZS`oY45o20(c8>4rSI&hvZZwh_8!0x zmA}uf1JD~&pn$4%y_-NipcR$$)$&{~koV*(1B&D3JhDLgIHP+B1RI0EOoN*KBLU+7>$J7X?Bra!j6j%n z;-AWyv$7=@kx9<*AL`nafe5SNldDVBXMS^gmDz&mkZ8q0vky=pQSPaDxsRj6BTQt3BN3*+$vr)M_^|3Rl3&28 z2(`lJVlWEnuwQEj=Le3$Y>thHRLw^~f0%Ifn%KfuUyLkB=l5f zqN&o42si*D{FGF;-6>^M^kAP*b!9vujumqp#t8*Qd`*;sVN|@K`13iYgS^s!cw=($ z^jdVlNM;@tKoGqV^gvciwF@>9P75~DCnU)L*n>vNC5O1}2bUL_WpOd)$;M7iNN3Z? z!SVfBD%+5Wd`aY0^jHFT#L@1PO1&?}%OWNY^)XZcB--|vbjL)6yY@HO;*@z-(A9ay zG2*N^M`$4M>|o!J98(eHnZOp9(AJ?I^WL&N1p)m@o}ZWfF@g}g{On^+(eF?-g83`U za?KO6@7e?0TEk_*y1<^}^5AlY;WwM#NReb7J(+svdT<%hB8maHL)D#&8IUNS8Q^uU zM3cSpk^~8iBftA#gKaGb<>KovE?K5b$(LmBOjtmT=AF`3N!##ZSur@kIa{7pZf=dvRe@4>QE$g+=RthvfZd+5TEx*5(xPfujbLpQjz49MUSfSt_ z6WLro0-<|GlQS9lLo16Fw0Og&_^3dB%fbjp+!jY0rCzerK5Q}TMXGg z?2xEH3yCh3C7aoZ?Hx?d1XGcksInwz1yk^2(lfCsM2ku1+GCm%@FnqU66aplrz|T` z<3Gs`6hJ&Edyx*VAv;Z9HzxU{buqLECmh|ZBohdu=B*RQe~%) z5#xbZxrts8)Vdpo5U_+J%7w%w8mvc3A9;iX-vn}9LDqWMkeRA&16KBY@=9oPG!Bj` zBFOq}70*wbcdG5tHz*m&y;9IKAxvI^#}pcb+|o zwzp|`_&Oqx0cXOR92I3;^4ZJ9m#&aYYTq1y3DXe?OwmA^%bsiLHlN>6p~Q**=JnEYBiW?uCT_NC2!-3efTCfizgQ3^1S=!C3`d^LB<{h63K7FTo@J z41Gap+pxx7mtYf-@lii%+zd157oY&K&=a#$RXc)eC&hz>cqgXA5u7sO{<)UUMJ>2b zj6xCRcTyCwN;6Fp3Flf7Kqkr=1qk%TD~)IpIc?%4;Iz|lk?2o6Ap1evDg}6G7z%C8 zxKOh-a$Xne>qts&T~n_22vPwgm%iDw0+sKtC{U(1g#qGJVl#?=KiFMkJ|(Yvo5WS$ zKT_?->-ugJ63;UV9KZGK9D`35yO9j$#mB0?8ULJ?+k|4=j;;aoSbe)+K9GApH6})U zPmJmy(l*QMSN9D`oZm0;*72n+XO$7_09Muv44L^FISKjfr`MI~kdAw+8t0sL3fRY; z+~*d@@jmF_ILSHnn740t(sw(=7{{R-_^DpBWVE${if`rt4ecK zAF*eATt&j)oMEcDyOEzVJ)8KSQTF5oH?1f#AC|kBT!h$Rrg)e?CGOZPg{>t3$-9vU z1Ll!)fooPd9g{@aZE`WepZMNa7E}>+S;$#%gmpkjunzU0s%U%-;d}@ddl( zp_*0+G@Jacgu`g!kkFeCTZU<0?IWX7otaj8F9|U1DYOo7AW3fg0baD~Gd`urZZMM= zgeyNM`=7+{FE+P40^-n8O7IWkVx0wsRTa{bLPnIwsy95ZD(( ztep|z_Z@6fkzT6|J&!NYsae{J@aDY@T_28F-0X1Q~5Z16%tflApz?8jAZ$p zQ*uvsiGS#t@h+uh)Mf05L;-u(q^0P+c(a_lYb89m{-{U>11cX!sjtqTC=5W>;u(^1SPhhX3l6Ucsxw8$)b?E;B0 zVJ?3D#qCFjTKvzp1LhL~9CPpd%a*+=12B`3Tc5rx(-Mh*i&~*5ycriUgq7;=m3Rv{Ih?&4N_t#LeE@8y0TnmAJu5V4QJ6RJedf zN3_3}eo&HUw3cAd;JAOWuR#yed?tQT*`@c~SRWhtWF&ewuPQdr3V6gQY`YCW5ddS4 zfUARYW)Sg=41Wu$C9v@}3Z5#_S)+s zaP$NbRCov^psU|0K|9r5dYbWb_HlIc8q zw3Wh+ae;#T9%#wW%AR^ozz2dRSEY=z%fy{ZIbVoL`pQfp=|_VBe{1ElDoaYxd!WW5 z@Q6Nsm{RBhFyWI?^N7R}kj+QGh~SST>yI|KTe+&#u#M1|qwV-nsZQe;&|B~|9N_!d zTL5MRFWiMU$TOg73U~|6eXg^N2Y{hK6%v3wLjqWsM-vt%B(!-~rX(#&0EsjDJ^^4! zsyV+_hH8vCPmhh#{m?t^XmPr-Bu~ZcNe=3r`W z=8}?J6}(>>i#)e+&hhyR5qE{fKmh`+qiSc82Xp_H)uj>-Ps|D+asH8yAU0&e>lL!U zKDN5V6pODwuz$?iPQj>ibP{ioI2w>@*y2K)>+XpUVvKBu5n}=9po0oK;16{zTC}rE zE6h>M?bqTCT^O*sKDMURyd>w6LOOT9PS(N|N7&9!|X%?Iv2+InQ_Q zuQS(H7AmWWq9d{%wAsxx4V<{2IFMM*eIhD837HX?+wT)V@>^2;ohgTJlRXVfibBvk zUCWI78u^jUnQ0O)CTdaXDTzM^Z7w?EQ&?{b2;jUVp^hLGyd8y(srf=8{0R@5M z0{x66UZ~c8|!k*Yj_7u7pN1)+A*;xoK}DPQfQV;YDB!>ANdC8(Bz|4e|y*1?KcX8^?~ z$KiOMbsHkiT^9eyl`G%9Cnp-D-HR5kC~UoT*BrB=pv^n}KwGgunW z$g{9}tS7CcA6{9k>;8cFxECb28}?GQ!M_2x@daq}j8rXY^L#0FkaOFHm+5AjGeoV9^QzfIcF@%yzlpUi71-5dXpQP|mvOnWFyOQs{T z{#^b{I9up+CT(Mr`<1HV4FdMxDBxwffI+_{Nt|W<;>Bn4p4#Qia)%_ybA?SCFMsy3 zRJg3r`hoezgw{~d3G~;f^fL8@SjFn35SlwJt!Sk*9U9fMMl!$`;Jbq@J1y1fDC2U| zv?$Vk@SfsC0dO4SodapG<31xm1NwT;9p~*xRv7Eswm`vqhGEdE$er!lgRoc82Hi48 zfd)VUS%FiRrE4MSTiXvxEK;DpJzakOPKn1!eDIg|9BeROkUN{g$1mm2M=gwH@lP-; zY1QpL4g~c%j@++ka5=6`Ph1%?4{8;3^pi}qUH2dz@H2p(JngG}r)?yqbC*7jKh zatz6yWjG8u2zNF^M(B`$r3;Y9^e>mGltV|aGXKk^XotQKfbU#)9)COO6yTmI_8`VX zn~->6mlR^g$^M9w2t-f+vm8Vf8TGE&<# zUhn9ev)ai)p~6bgLz1PMNU~|EB!lFa2*SG}HAw~iMw~)%fE+l)y-vk=*Y5~-3eJ(- z3~LqThzgHEkqw4YJntQVBUsdKCQIcd#F{s|W>40smV9aBaz)g)cMr}g!U&K9BLeEA zF9r(w`aFafS?f)5AgQXp+}5j(3X~PValfoRPHv$zy^NR`MUCyCXU}FNP4Ab0O^OlP z780l_Hn0lL5Wu8b)?}YNM=rA0I(ii7{c~fxj)14)Q6PJU>W4!Ba9RTr{(fqK&zs>!LkNCpZREcP*Iwa*R(_XiTA~w2RrDL>*kdu%Gd(*1vn-Np#_6`g2_?O zS)gUcWKU?`FwYiJ1PHX@AE_qOy47bxOu@7}o;QrbW{GV^o$OnD73Y>U1Uz;TMOmX? zN#cNzCIDfBtZSe(Ww^iaGYlf&oQzH#KWq{^4CXHh*Fxe#64H~!XP~3h=$r^OD(8Hq zoMBpaYX?VEeI^{+gD(cB#bW?6GPKV z3jl=Y35d)Zb&S|wRI&^}g7OXs#O3B|atuH~Qp6?2cz6`^+2GJI>G9i~nQjga9GVpf z;t*fAyik!i94FI^Xk`P53p7Z8CCK3`lnbS*KR7*7RJu|mS=c>G%L$+xVqCPQ02)yF z+%`j^1=7kyHCk}OjBmyik6rU8a+K)=pdcxw-UtMtcK@p7RVhXQi2S znJMNE0sssI5JVyQpyh$z=}L$RD7bt^^}HN0`vjchva1qs2@!O(8eWuycDqG7A6!=a zE|@XUtAiEL0OFmMm!X%gUF#39fx!F?;*<8s#e&X+d*zw;06+@!l7xV1YAzu~PvdfaL=2uY&NF?sxy$eIy}Y-JWc>i& z0HCZi0{RQ(eSb*6ngM5v=fe_!kciuHBDjYE;3>YqW-Cl|RAi}Q^8C>vA<;n0eSg2B z6#}3Tp8(t8A79(6Y)$+JdOQn#kFat9q-eiv9|(6F8_JFHKh$Jx1A zx(=@hxDV(I(6?OfXBZ}~dzbY@FqcX=1tPRHxL5ut1&PKX|IhY<-?!}HxQIyA9<#cw zZ@%W5bVL%AD@74nf&od1b@(T3@&Kh24|HVubYSix*kpU8D6lYAZl!_agWvyj+)kyxBM_Mh*d>Oa<=mXsCrM~GWGwi{Ek;2 z5)k+jiCMOG4a_R4OuST_6t8Xm-UtJHE(%Rdctihj#{5fM%7x-Do(<6fId&PbI|W@N zCWxb+lp_40AP(;GG~xp=6@ViEK4Rro@dbx%)JC$bg$)sG#&r5i<083&dWSG2;0t3b zx%ko~aQ&B)R+$?m5&QS8`)B-lv;)@4#Rn>g6X#%1mFMych9Pu?O1=B|#y}cOu)#Sl zdoI9Osc0hRivt9#gYxOksgU&0W9ut;#DRLWq20{a+K0z=Eu-ck;OvW0Rk!Q4XEKog zD@$|Dx3^aXoVGopRf-8s2mv>|oloewueSH9FdpN4d5>HmRvLO81oW`5pv4CRv8RG_ z`mzjj`(FDduThPa&qh@0tgL2){^sMXSZ&5&re2 z6~)>@0^kJ;F%Z>iXwHACYcoBg6XurHCFb8xS))NBi8FhYc471d2~Z19j@Nw(z{$_0 z%Kn52!~V4DyrR!QvioM&;z%CL1$00=?3>#U8oLZ}Rm4v}DZUr>G($L^kl>U882$p` zW6m8^b;huh5atM2dX4x}SR8Kz=~!BYL|2`d#C+|n7`$vm)V7)G!%HA#y$#51t& zh)ISgCsivGCKp1-2-Tu70^q(i0Ph=?{6g8cF!o^#Gjs-^(dFL&V`*7ETM`@Evp>1I zOo4gy&~|nYD%eC~$r(mdWrdTW>1vsIi9IE;;HTD=tBCgZcT_7V^~(bd=I>8fVXhIs z`V9;BKjXk*Q4ohsEJAh+QW}+#U%&TMFMi^=)-LtAN2Wt1-L=<o1nUNeISAIYMyp9l_d(bq^7iu5fBa2=>A=cFx?H7e0V0Y=`rsNnWx+xBv0r+bVleg+ zEQ4p7J8ZwpnWR!CNe`o2UN|IAYb0&r(#e_To=4jKxe^4;7E4hw!vd9*? zPQ1G(n%d0Dv_uujzgr3mv@GFIr<8HoDd)UfnS>M&)(PN#U1h$x9)`Yv^jif$Aojz@ zp%2sHrkjOCz&ExZFkjxdT!73n^OL=`T3mcgz;4=vVJ%wa6WQOi9RpPN99}#+NU^fl z-Ytccbh%?G0E7zbXf~rb)4FF=_CbM8`g&IG-Ngvn`D6HP3Q1`9*=_h{+K6jZ8e)De zUEH-WgE+^Was6`nB5@q~#FcX1(J1+^(9MccFpumWi$bFT4> z!2pnOo>M+t5IDO_iHSbhKv1kK*4=v;z~{U!-O!wi8`I}%(1`rp;G znh&ikGW&WD1$+!B!r`@2#kV6*An|5IDna%i>)MW<{c-_8KC`ymWX45n^_T#T_JBA6 z^S*w{8f}w;ec-RXyEsQHQs`MQp^)QCWr=D!?Yw+WtHeCMTLd6s1U)ZP{g;YY zu>>nTAKG}(gW>l9s`0z~iZDpG! z49@eUdPN%;nhe3}m#1yT#r*Mn2ZyH}Ej@tiv0)L~wg@ZfN4sm4jbN2AWsJ364927x zz{EhbJ9GcuTh*k@Yo-X6Bqiv?Mq-(cs>Y+>mulcmIYe$74mIk1&;0`v6(4k4PMP{PVrC}yP|$IY!C-5lt@6c< zmF6}9k^b|I{i@M+jl>2gNYTfa0Xe=Jg{?RWlVghmK;qn8B85O$nfFQ(KNcb&40+ea zj1-lAU@2iAyeVtuuBQhB@4l*L1*c)CJsqF{F2S-SiLC%-`fQ#|Nv=iFDmOmHJW$;{ zuflnl#*NwLMu}lK!|W=G=+6IBKNlnC7)CUc-@JjAQ%)A)OcvpMpTzldfn3C1#OZNV z3~>}*VikM_MLfD1c9`QEK;qnAD#c0G;V)81T1?uF+&8bwdNUB|I@w?Ntu~7pla>KU z2uclDaA3n!b0Al%=)aOG{#;d?F!>*Xf39uL=4R3n3r{P_VJ8x2mtd|p!+BHH3Wl-K zePQsB=>EqW2L!B()yZZvB}_au+8v_9W@ago?EUYX2ee}4^*zZOg5F7h$;7X-ax=^h z2{gaEv-+6zg{=e45;nrfVY?1$Sio8fNrnMJ|9jg3+xH*)oij*fk>Bf@sz#t3A}BVBZICJoLin6nMMQSb(@6$5CuhF>GuMV!IZ(E zyWY08Ou>?WdTp<=2{5X;BrPQC-`iQ^cV#zG)|W|ok2JQKyY@AhK-hu4A#;9ywt1%6 z8nNCH32F~J85J)d9Y5XX^Uvd7(RTp&#D`(DYTh9r_RH<_Z@;9qqQ}CdhWB}i0QCn1 zgkT!WY-fw6kS`yXySBEM>GZZ6y>8 z@axY4F1)uaFCYMtOLK&Hf-?onV!nF`b}oSXN-41X)h5OQtsaLgOlAQI{zJ|ttc1_5 zuh2r-hQ5uFRvNq6wwAGK{S$1`#K#9))haSFA1g@ z9+CSrT#Es1HuU-PeyHN1GZo4@0t^I!Ma+biKpIuUry4tTkOW#$v>v#MJ9uweGY-jq zf4wubCg2c9#7Nu$NN?8w5p7M8YCqt|t8KmJ2fJ!4VX@+e&dJZT4L~9}96MrdJVVj$ zI|4SYYfdp&3UG(M*$tCL=H-rVv#0lv{yjjri&>6)C3*bifkqQ8$xxo0^|R$O!$3bL zKg;|=inUCFWd8xE{o9s(<}>Tc&6hST*OJ>qwf?hYXR|;{3C!2HuisOWV+JNC)h8bg zGU&qnd$z=S4C$aqaOeI8)t6L1KTUBdLfh=lT$ZWYn?K)Eca(L&UZA!91C>R}NFgUW z9zi4o^!coIw(7y|yIw$k3ZQ6IE`+g6!E19%s(?4idd@h13o%X=F$drlnu~zsKiN}b zUTEo_b^o3z1<;R5zQlb)+Kq|8Ufv{iU&i@c!n2AOvIgr)>$YT@d@YnDp_Y7 zJne_%)bA3okSWKAt)uaWZ#XCgUr>h;7mrPA|gnaBb4{_m>9EA18exS z1P}y-^#aC~O0w^1B;(MjXG+Sc1Q6Iz-3BxOY=Soa!*^{GCwb^?l6`dnN4L zf*h>?KO~pZe5XXRf0?MpDAAWBk(xSUKb;;%%!K&Z4Ck-{D-1>!?Ko&G?U`j-3eT0X zytG(SX(>%k)cc0u7d9I#1cGQQ+Rk7~?(Ve?WXzD)i|(IKt%otoZK71-yPx_TNm;h4X)=oPSy~U~xI7Qcar#dAQ>??J4dj06$tU zLK#5XES3G&C5WC5U~tSnM^|FG6w#K&M{5C%BzxS6J@8IwGeUeH@U&IddNM>>Y1m_c zT4yZF&^z9ar^ck->njV*qxJ22Urk!HM-krUez0rsiMzytmBv}c?;Vtc6Gkh-&R3M= znJY`fRKt+CjCg=)msF))8d_uG$7=_N1SB3Zg|d&nu%W_yY*ncaclgD=dS$ENN6@n* zK|xaD*_JL#pAC;=G4PL=a)_+LFZb7nM52s2!KWtwrFUu^F6X z91aXhC6qhQ$}UOI#gZV>zCmzfm&H4${Ml)o1TOlgnmcS6SQTsG#n#cua4y1=Et; z4fZB$Kk6l*B>-jWQKWn_07Y63n1V=U&tK!&P=IM28Z$rKU1KgT%28Gk!kMrR_Bto2 z5nz@H0JknP*<412O#mzO*}l7@TD4A6qaz)fS~ew16SC&)CD#PPP&D>lvG|KXoGS_@ z>m_kGNh-xm7|al0jmb9nVXDC69{YwVJdZZSYdrJG)ulRQ1l=>VY63kl#M2P|rtK+P zz+S7tLG(75PzN)8pGBHfoXgd6)(}c3CZGkN-Ue1Dz(a(M?OJ2X>YLIL;$MBWT4OBk zbVS)#rsZj(L6HI75_~7g2JZ=`Mv=y2(JH_T8}$F3xKj0U0P47xnUXnSLjpy8HPrJ? z3V^2zz|P=+I!58_SEXSdoz4D>{Eo5ktcYU0E~~jFVcXU0V1KlKe3N5STdrvfV=rmx8 zv5z$@Jif66KvDs}qnG6T4=&XY|=)z3yNNPUfXlZ7r_e}Agr^UMsi>`qD)nfDS!lDb{K#t(^c9fNdL*6 zx_JixoEb!NSu4(1aZ;SNzHl+xy?b$HfJlgp6TI5_`;^iv>(M@JPoRX36nX>zpY(-K zP9IU!f4h7x&H{-$fjYrl!{CS|+E4ux6K0PjQYU1m>7=zlwCA?SJ}XQTA7Ei#4Z9H* zBO|D3H;xUDP_`(2=W))~OMG=LQ`-6lh~IH1W_I zfYJ3s3H1Kz6vBlq05cEGGJgdd2?mcihe-TR04drd(EpydbA~tB%~xUUI9-g zFQKD@9vum^GH1iWY-3_z-0LIGArql$R38zIw{jAnrxEK^3K+}uz*T998iW(yFgT=T zctlyG=$d%$ar_2@L0B``J{tve0+>Em&JSShLiB(ZYJh{>^#HzB14vZJY3b0HB9CyQQO7{6&j`bRQQ0`-961m8lB|jh{fg*XlwT`+qV7EBh|PKf%m|z0nf_ zLf`4KZs`4S_o2r^3vi+&lCy!Sv__FAo-J{>=jeBm%O|Cfb;hzxDO@xMZLotK#@@0^ zrwY)Sc5`ArCKZlZsMX53-6q8VSlLJv00_^;q~gA2Cnf zy0~zed4G9<`indE)%gS-VUq&T(ft#wP8++7XF_3{cIHq7j~qZ@nu3KCjsP27hFr-9 zB;G??7@zfUU7L2oTp+Q*R@o1(&+}+Qn--vQ6Jkx<@WMp3EsNp^lw;-LFXVcUlxi)P zp8L4-6Xjvy1a$gfurXF9dQQRRP>l+a?q`hlm`a|mCNul3D{`G_+71i z?QGEzVVXn+!*Ci&76j)wMyi^dBtZR*Tv)G5V0(o)DU8EKKkZDmIwM&T?JcswdCsmN zn9aX3HBrY=dWvViyC_@raQ64vt2u#&ik+2O96D_wR1oZPK#VsBa6_Ei6Fed)E|<#V z>+LUnBSt{KXKy*d5%xFbXu;z zNV1+}Ghq5gXTOPB7iQK7D9yws@@IZ8U-$r$4NtqUAV=aZiLGp0HpO9sd~q3>9i+T@MbVZ(V;~{l@?UuYziggUhW}c<4fe;BES5T07S&& zFp^xhR$>zf*>_p8AJMRQq<;Q=%ySuHdyF=(@<;%gp7iyT zS1WCmM6};W@ooS<+9e1WKfbEe{QagC=4Y~YOlYw)X~)6AIAc=XubDPvQ^p1u!I@wL zvK>|-!5u*-6#&{$9<6Uv(NrqB==Gc;0XZ~NDmMQjfdQ1#emMiZvObL15P$;352dkp_xS~4xEM(8KZ95zi8o)f8I?CG z+Dv-6`#{6IMiTpKtaEfkXeyxa3`y30Z&QION2-3GT3eX)OzQnb(*z<*EAJe=TUUQCWYJ_W20t<4h>?=Sd zPAg0#D?lGWYJTUT$-qoE^w&0Krs;i6>sqMra=0VfC2rW-)vLuQbn^E14QWTkY({~Q zZ(%~OH=v0`k`{6%$F>|Nt!(6WcEK7aW~ZxW+LvG2Y0Fur1?gJ?3hJ;Bi5VygAmILR zZJV|wQjo-tAf*02DbmM;N0{dYB;daCby_Hl#Lo(-P?{32tRo~9{58(-@xxhj82*ld zPqgs`Lhp>i;m1~&=nwl62GE(kzp6>|a7YcQ(NU(^ymh`Az(fAX9(!W}V3}o^2dp7bF0%koRyA4VLN88? zcWh93Xo!#U=ZxQOkPm%jX}%^f_e#RqCJBv0v*Insm?cRY@Kdvwb`=(dsaOQcSCr&y zwHegbUl57+mKYhK?G6A}?u-`x4v=MN!akPEY)0_IW`qUNI7k2MTm~bEWNx!;GHA{) zMoy7x6#z!V3<2sg+5AicqwUSJM;nI=#UA9lG`T~9O~0!CbfZxcT3V^SLFwg z^N@{e0|SMM-=Rqxd$1!MB7vTOOQ}3R_8-Y&t&a%=dQ>p-2tWsA-yov-xpn2{+KNKe zx}nX*PJ5$M0-}w}ouI2d>*7jP58K2FqzV8iiXO=g%Z+OI{I15Kag-$fDOoNBSdn@0 zai%CKPR}O#3Tq-A`Z!d~!owWA9v67w>5=$b&hvM6R=pJ{IDaNkH6ZphRWFkNU;*TI z8_(U!=SdqH)hw8=fak4K$KFeTN;fSp(hAg06$YL*_2L(cCAcGr^M@@-3wgA&rQ2V_ z_tL`pd`q|4(>G}BjGw|ocyddQ#1Er#r`s_Flz{ODx!>~Tcl>luodk&1O;Y1LG9-jM z0Cv+4BnS>hTMF7u=vTso?HRM~^WnMC>=~TL-OY0+W+d^3t#v>^_($qn^=!iCb+Ln- z^U93XM(|I2AMat@M%E1p0nDmpa=&;^*X7Jn;PBSI53qnn#umAY(MJPdTc44v6>*As z6mQXVq#gUt1N93QD2ByB>@qYNdUpuV39$d=R(p$QJWKM2s0as#13f61008)&$Cmem zlth(tVk<4HSWbMjwj|D0D5Suo&w7S)gPa6?f?8D-1 zoI#Wte?pOssj&0)zZ8>r_pl*hB41dPt*z+DO!Pa&t074=sBTiDf&sorvJh(mJTl;g zp&7rC^?pk7;rSQ^r^8O^n&ZJZkLToNYpfQC*p@r#d;zP@$jvm5)we5i47Po=0D=HS zw5QX3h0MXja#zuA%2{Urk%zk`EB-|a01UWd+By3ZU%}d3E;%;wWJyxI?!$gB=l?Z{ zKNv7Zv1z`=9?JkEoEV#WAw~jOEgxpJAiPun01~JW0b;C3Niz3THOik&nokI7m$M;8 zrx2ja(96xx3@Tn zKqN^iiIY>zvI>LQQ;N`pRoI` zkfiK9x%>(E+_xc9aQcfUC6LH)0!T-+8q9PJG%r|l?m(vM%tkTSBt(98by}KgAuy5A zA0sdU$aJ)b`}Q*lDmERjtS4G&eFeMh)Tmgf!GJ|f)M}r{p5hr(V1U_RC3xgAhL61B z)IG^dTzCO#KE|^0dr4FX#;v*ssgfe{j-F+&kM}pvoP>@Ros*>SRV%;|?f9<22Kd}d zoNf6Saim?z!-$*>fGS^E!SgW&3!JZUY3$noBp8jfX5K5Gej9({0;U$o*#c`qSnLrqAYuA+Jn?K7QfhqLXbDUI`8G48aUL)3iBYH*g4ptIk*-Tn<8GGbM zyJ}UkgsE%fAQ((0Ch-y((rVe~1t3B;1f5ILN~Xdv~Aq zU;|RnLgivXtd;*xZH&^MJ`(8bD+j2fb1b_nS_=3Kp6G=SUC_7TOvF0M9gaE%<=H z)ZVQC5@N?}Xhc49LUwfP&Djo!gnj znG3SXCJJ@9lT%{HnPgv55$cjX|78I`5jBV9_Bjb8?yqi<#J^7iLvO}eaIp0JPbv^n zM;ESsa8xdbz_kJZB1Hs>@)QJPEPABH`aJk70LIgE)6Hv=98|dp0Ru2HSqsg$Kh(7; zqZM5+n6o z>F-@l>_IZhL`M?DX&vi6AhA(_-bKLxkT6)rnjfW=3B_>3^Le|4Zi^+<`uVy^c}eam>TI>h@*Y~ zOnIIFPZ7@X-cjOtqvOn>gQ_|)YuPyl>K$s1^^l6&~=ixkUY}9 z*D^Gw#RYsmv8_WpF-QGAAQJnr*$KWu`ige~N5pxjNj#1rp3j=|y{&T3xPx-&mjeDh zSTlP&F03c=AAnZk_lXeIrt;Mof?>62JuVsm7C$l59k)S>51bR?^AA=Q=v_zbyinF5 zK8qNcyiOU$PPUq_20)2FyiE%8`rlG`X74RzhXaPgL=0sie18mK^jAB30tSeYPxA~B zw08fUu+9Pa$m_Vjf3&;CT;l}u@u@JxJ%#hx3xGuA-7WwLBS&ZN{YHwG!!z8&0F*bb zC{j>rKKnFcU>rpu&ny3zaqm zNecxrM@jro;_$hUXq~#EG*5qyiU~%w<1-*}VU8(6u1CbfqGCg!&KTEzr`X_Z1}*y& zatIM5q^fd)Y&sBobPQZhmmXQ(OH&a0iKzrbjU#Ycs9-c}NtFcE_h$u^0JK415SqWY z2@tG2YeJjb3z9T>+mwKUJU{yG5gI12>k(9)0}?jv7>Q1=CzSIu^D<3|0393>o+&Ih zswJ?PR!Oy}-^t3D>%vEWhC_&6M6XR#HK2dTiUZhTvxq4U2Y`-8%f;d{s;4<|u~L=F zQD#@`918n9Lnx9Y%&g}@x5So!mq3S+;#&^kciJvBYOU?x<8b3zOiVE`+pbp*;dmv46W zhvb9`D~vz__G+_*6Z-%zIgjWTus61J^cnYENFv5Gt!3gj0R&E8=12}y1Ix)sqt%ok z5I;th+S@rHnB#rp8@L-0-}Ln^Mk`~kq>lGOOXlBfu2hOORj+#`(d1l}CdG?CPSyg$ zY-fzbo#(K>e{e=%HCP67;-aD)^RUDdRtCmAx61`QVvT>gyH3GomyzgDNC7}+U42np`CPnnk`33crUs^8}e0ExC-u(ZT@e@tMt^4{RFOv-1ZYKlJvWCcJc=_TpM z7jmDSoRgvL!RW)G?eSz|r^+9|+sMK%*8MJ4eU#OwncmxXC^xt^-sWoCvKu1ojdB`6Y|@0aNc_ zlYVe{kv@M|V2C9@BS1B=;vSv8*r)BxTg878pR$+F&dpM=okA`DjZb1)Cq;)7veLDP z$lU;7$aAlh{DkkN5D3`x6)9-pV`06{75DUr?>|+1`nZ689ize1Ahg0r%(*MAoajtW z@?Oak`z9tN-+Q}e5NiP$ffyX^*FzJB)#syU!#VNB5Sve#%B7R(tI_s*t$Vl4EepD;?G|X#;J5E@#Uf0=OOsW;FTMM^~4qe|2YymrMS5>)KLt zNlBjOZrtD6QKT{=j@Goh_Sbujy~(oy^Z<<`ogD)B3jiwciJ3kYd0&RCBX?@uJlKbp zF_4&o#(P|-fb^uZx&i%)gddF}di0=rohv~DDEl)28S>9pFGJcf4{C_!V zwTfax!6P`E>G;D`qe50YV9gj{Z^k~W>bHMt2mAIl*?Rb{U`~ff6)Qe^?X2Hi8C% zx5h&bV3TU$6=k^!D&1eztQQ0TD1axyJ7CW*M;<9MyydQFQU4?qDxInCc(8HTZCneqUNh(5qtHI`POId*2$d4sb(j z40;AVW5EOG02=_p(1NtGWvf~4{{(qvzjrb!V8T({0tBK^M&dme@W)+toh0+bGx}a( zMDMH97{bn4I`5rgnUxbPy&1{s)nx_BP<5Z;19O6X!bGkQR1``AIj$NiHF8Jo5g z;hC7SkC@K%&nK?b*Ugjr85Vby++W;7|9HYGt$G6v!Jr{dM62U~n{FbY2KrC1nRt1? zrWlL&g%!rxq2NRD*-EA|1?=0_mg)1lPhzvjh?!@|<`{a@s)fH%^M>Rh@0X%EEi8zb z$4O!GIVUrcxZ!r$v$qIfx6COUf_59v79ffQ6EF`y`@NktJ{5b|`xF8;NCHaB)M7SQ z(LByVtwvZO#HXx*x7JGvHfu7HRab~}MX?*!(&Yj=(E&rp3-1dt^OI$tk&CeB#+`-u z{Wbv0FxtoiXybiEfCO5lGGxDSu3-T##PGWmLKeFQ68@O=fRJJr`_$6_lvBhXKHd8k zctl~1jt?$}m)pA)cy}3vf!|7g{BT|P(P3ephR;GfD_8IbfKFnI$Hf=_4vptoKrxn& zPOn=fp5XlZOE|F%K;nZD2vbE}=tyq*u=!9_h&YG@%c#s}%AsRoKw_jk{N@fvyOWPMq zw>>MsD_RJt=)Bq8cgzNk(8`xXX9TS(7(tiH>ymsR<_MTTiyHd~_J&I@1F1pi@FZSn zD*yx%aFa|k%ADR_-WN%lw?jbn4~ChY?v7w#YXHVS@;tc65Ke=ZPDRVL#xRGNQuql8 zAU?ge!u)u5jn`H~rosSz;H+p}`F{e$0p309HUV!N!N|mN;pBrrA4`l`V&aQIwF~VNSr_8mFZz8*oF!n<(xt_e&nzQ(;c$! zho&6jy{s?);}Ss2gciK)_-Jj7<0H^w`18GWK^eUp={tC7gFu6Ec9a<-TvdZQF8 zT4bNQ7_U6f$E85>9`S=rQk>0b8KZSFatF|!Q2b9p>1`_WbhJbg0VIgQUuJ5bBN0`^d?ze^NW(pu)gTIRQpjd zrDK8Omz~oB17QGWoV65r?juvyFp0yQBJ>k8B=9glXrq1EscpwPKPvp^ z#rQxOh3m4E1aqI{P=PY;BD_GCTj3vHjNtV*#8151(epMEi!#sL_8x1D9@kpQE4Wj@ zKZ+$6LH~|v)6ciIDrVvfqnW%LMp}WkDf--hjj6atwtYVh;EDav8wpS!fUkbsVbQhoxG| zVcXL?pjDZ6ANvd5Hs|h&?A5#ly5ujxk;X4?Vkxnsf-Zg8`HZrO}dR2dyg0t090X`HY28Sbg zGsmq_6hKVT4#Q7AEES=3QqjL@MX~w5006F}0bhsi1w#f1FmyUhz}T2Fqh%&Y3I673QQ*nz)x_E!BTp}Ir*kJKvDzT1Awk20_@lcAoYwo z3aWW>m%1kd{C*~GvA~lbxP-8BTYY;?5~?v7U_Hbj^whu9{ajT z0176s!Hjxs0Ye9Z61P*t z!4}Fv`nQu-D-GY(NLr7YiEELEkS?X5MB)U|7Z0&?m}zr@ zLbMCOh5@06n#wApRyn{vCPvw1urXj$*Yr^amW3Jpp*2?v4F@gkh)&A4{}1F7)X zk>oVUc_aD%tenZutgSHL*m2M)q;7oHBp`shO2G18ADG#%*9ge?-{KQKv#!ki=P7HC zPW9tnpxqq6oL%pOf-;XnDc#j26%bLC2Yrl4&20R!Vea$hEJy zEM0=h?osRY@n9*A$@6V>D?E|t0mzXw0t$1c@|*yNH{-6vmlq^D64D(W01EBu6aZiZ z`x^vWZLW!tDRTf~^D#te8CH-K)$Y*Satp%Qw+rV+#rcd|%{kE@+`5s5C7BCoG3RDhCp zD_8tX`z=~07f3A0_kDk7jTZ7?Rsc2telS8B5n)8MVLiZJ#?RHbfu?{ew86p94K-UK z=LaCyDkJU&Wc%*UYV)S-Q|=RV9%)&Pl^B?$p1oM;)R~-3@ZP<(hMjzxJmU{ZVURov zzZ)+AlIrY94nrHuAEkgb-$}#B7yxYVn?Yx9u^7G<{UKvfYZzyT0sstRiiV!KA#opf z>NRBr;&;-_U2-??=;~L$4ukJ~k~_`CZ00Qe$Ej=0UHcl$(*nAV`B`M40CKsUVSX2R z=+C5Zj4#0dtrb7`r41E&mcO=T-!ZZm{u%`(eEVFQ!HdDxrlm;!IuuaG-O;$xEoNe$ zgzNO>xdW-luy7?)kV}8{mED%y_Y|d1&CSpp$sZwcO93Pt4goj^;Y|yKglUEQKM8`b zttd1%iNmC_K@yuURTCaKZB>lW?ir6(g?iX0IBhOAlAVWZ+m5P_^GulV#XouY6zE8p zi^PF*q2UmCh_r@$e07=m*s4+mt#IB{C(!?4ZJ~{lG_}c}bARxLzfV{>-)o*)!m@6bF06?yGL0$udowb1id9$G5pyYsf z1jHmVfP7Tpq4v|m45P!{S>q7zIoPE78}BK~l}kC(TwI)^>vXSF&pg>jBwJkE&&$PX zr$E}W_&C*_2;`*;(~Be$!1H5L;b01ryXF;wj6czeVX-D3v~B`2fq=800p?slFT%d8 zB^4Bc5LYEm+Yx0QmrHfTP5_C$fd+{k<`N!swT)R}w6T^-Vn{M@YIeFZxad<}=<-7` z{`C5CZHN0m0T&MR1veRVH^2m1d;--g9t;CiWv?(btJ{V)AbcM|C_^5WOGTdn4-Zy1 z>%GbF2wx2$Oom6y2FAb${=1iVsdfYGmUsz#83UW(oK!^u;`nETwhTkj$6vyZ;h(fl zGOCvZ6N>k2K_{(s1U7T&ZQ$RVgE(MfHbZdq(3JHBfGmj(Na$Rw4kqa^MF~KIn1vz= zfD0Op-VA-2FNlv%mGz>c?8yKlG5?3nE0r1j+uCLm3U){UGqfJ8HtvZ#_SI{9=B099 z(PB-a1q&lriZ}pB#5?$UfKp;L*wV23YWjyYCJBU4>P+uFPQaC!CfHhG#9@dmBf9Tu z|HoV+H9;Fa>ZAHHLiU@P}fw%NMwf$D&;1e?)1t!`fhz*$dhy25| zwxLfd+X#7yeqEPilQQm0lAW;-yFcRagW?0bESd}lJA*h1{W*lg@v|^Lh{a*ft8V4U zxH(&5S6asI7T@}ckL~FAX`VTIn)nS?_2A^B7QmTEIOv?lIA0^ahDptx5Q;i=z8Xbs zv~Aprp$jpWfI0c)IRXyCymL1I2!Ol&y+aD%(7H$*jg$dr-B4Ktj!c;6 z#DPpXE9U40kZ|WZD}6O`*xxIG2#1V6XJK&o1Yx%dh=gz;!8D?aBy=ux5E1F`N+9Q+ zti#0NLJcLjN#Cjb|=d#pEAY@X9bSr;6u z>$w0-;1EHtRp3t!j(b}L)H^Cy1fNvBJDf`N?l!H}mN9M9f|i~d9W73ATw4-7pFUH` zaCmn2>k{O5joLHaZ6>3#aGC@qXd57Y_~Dhtd}~LQjv~i+*615W%k70wIhPX^F)H6Y zGp3~+c1Duy{TJn|d{jVH{@zs))2SX37y`7~X#|_G5%vH`1knWVu-B}bOazM#hfk|z zL0O)8rnSo}c!b9QFyJ}QRVg5FyLC;u$&)?%{arO71s-7pCtP%kea!D{)-QJiAP7SJ zRs%>>>6xC#q);?LXibDMOTmSGNuk15Lsy5d{_)vC-BDR9lIVsxUN6izM7m*y@=xwN zTFu6lRy{FiYe%{>km;La!Xt#tq}jq z-m$8!wp@}@00Yf|LyRX~K&cKq77}N{TmD8j=y@G*`=dviDm(q)a$loD;O^m=gJZ$s38w(KqxbFCGSpAS|R>hLMvI}<<$ERfisZHoMBu^Vh? zMt^k&NH37dwKr52=^B75B)YZxb6*jIrTWO)C9kI-Mceu-7Whbiju)nN)MfJCAW zjg>KfRTfdHc~GFaFFGg6kT@fzc-pcw38E9sM+DvH(nJ)Iiw)fzw0Azaw$yxRWs%v| z*>85p0m4~x=xOKqx06@vWs4>SfJl}GX!TkNrstbO@{v^~ituxQ|AaWR{ElnoKwc(@ zE})4k8Rfu(Oh4V!VM58+6tsQ8P(in-bAc5W5((Hx&?Euuws!SuwF>I9CxAp%b(er~ zw40H*d;V_xY0nJ7IEZQ8SfQsk|8EwK_MNPvhOk^IHT>QA;6SQKr2`b z+vPbw-Q2AjRi9Z~uE3h>qB!i_1DqMAcF@iSqaa4k2x8^#*BUJWm&o~`72?i)4Z42_ z3>66UgB>v+b5w?pXhLLl8z8}1$Jm6ni0rYbw|`xFs`?x>4EU$FDH`lQL|Z?(y2R8A z$oIe74rp@Y%`&t_rA(@{ampIo(PJOmYra-2@2z>*ULpEm8+a;h7X+VHj@;(_0Uljd zmaDCfw31L2{PxZ&Qz?lsdNDs0Q2sq7%T%a(E+g0=F_~#Zu)oGDkl0Mjvptc?Y@>%A z74Vr1;m|Y*khTolKWJb+rutqs>~0;I?)JI}YSX5=*UcB9xri=Gq$d52HNYo)a!sjO zDaq#dBq`b2dF&kvNI=UGBZ#>RL#eftJsA~l4}gU6S*fV+?_XS6EhL^K5@=OfA#U$! z>@s$N0!D7_5d1p@ni08YXG>-&bWrdk)?+dc73m)eSO<$7UqhUm=+=fx^5shcM!Zac zr0G!Tu@7lQw{rLi>>XS(u@i%5U>Pyo<)K=~^gzH~TGU4+CIxKp=iE{#MJnMe$$##* zN%*%(zyT7;P#kpNtHG?N2m(`lz#>qcJoIN0n=zUAZ#OSD-``QKqV=AqiDCgcBdv?H zntwoibmTlHxZvMmvb`%WQ>SUY(%yY^s&a%AWI|IGrd5;V9{=#_K4p?%Xq$aZ;{7sN zN2XRjT;FOL^OD>TX4GQ06uF1l<95fhtUW%T$%qhCel4K<*H2w@Ou-0piz}b-X87;2 zlz1sNCYk43x^=21aso_*|H+;(OV*ab2p0&@?T=Z0!*e-3-_mJLmtZK$>aj;|zvJ1~ zPV+4}!2hvrzxhx3f&l=KlNB4KVziV`sNgf1fD7WoQlHy@?ElE%}xPhm(gDeE2<#`srg(^R5~vfM-;w5 z4H$w{#!XYHd3Rxs(w(0daDp~Ms)6ptb_R{73RakCZ6&~0Lwh1UCgzy2vUV7)9$IA% z@bo{qC*d*#e1W)K^$L=0OBI?zweb`^5wQ(-3LrF zdPxF8*80~68?`kF-2;CFb}Pi^u1mKU*!9KO8%zW09-ExCrH=MY&Q42+?jaqqZbLMQ zZ=;~)vJ|I}b8jVp$rusl5GUmsr8uW4u@BL2^aPIpr)iZqO8{zG8OP*zL`T>M*v!Z5 zyDb*CZIQqqMoO3s_bG)T;$LD{cVL2@L2+hC#qMW+4dOgx1%AAzR$E@bw6Q|97WecX z(lhL@wiR(Xi4HMHsOjo39E2Res^XtAIm5#BPQP(^k+z!w6#u@)(egy0kw2$>g%%}$ zi@Mw$fL-V1XKRdMhvAEI6RdAP4WzO6iEm-OS!u{r{A_QHiIz2{82c_c$1?%tfUYot zzRff|iJ_2_nHohsp1Mmo+!u0D;$={RT{$y{R z0+&{SN8~x^cGZU<5bUp!YjoP&HNqK15A7o>OUzCI-I(~^si6CW>ixPKthp>NBJJQ2 z0O&v$zi8HeX~S|QZ)7;x-V`|T&sLpPBxIr^{p$j;#RGA0*~n!cpn{dgSp>0z&isZ1 zSHIX>Z@%*KPV==b`_0`4nv{*i0Adoogp__!es;lpz#|lV39zz zLp1TE>{MkoK_3qn2l~O~g}RyD4W(Mvj#YbULdKru>NwVrN1)+fYkYeJD$pJ}&8@EE-x zyIO$fW8(G}wFs}BwpBpmc=4wd$<~3?E`#B>%&9Xr?lB#Wc)-_o4SW)@07CqL7GFDg zwN7FK?T&jOL~a@-M+oeZ!x z49qToA$R5*ZJzAoPpv7_pzkN*w6^#3E8xnd`lJAz=pVgT0Mz%E<{vY%7eix%^SWMw zzBiqs**Gk85I7rl_1zzF3SCFr1T+#*M=J<6|7s(Z0DRiH1zD=~f+*>-xLEUQ$6MeK z7c8xUtDU3ZY3(Gz^VS-K(CH^umzj0x$;zVK7lHw{%cy!xs+QE^)kW~t06EYcpk3pQ z8Cn}S^Cfbpq{vwUlq5*y&$#fDB#EQ?SDBn-mP=sSDZ$;Z1h}QJ0*#r;hm6A>_L8)0 z75_OZK}VdA`Z3&TRHwEDVNxGpFF-STgPhwJTf1ld{*wjReyt<)G*DayfMbXMq3wWR z(KXD1HhKIVbafXy7Di~__>{R!DvSiS6sZ1dTh-e%+tMXLMUa=h$sT%6;v8NJP%bWn z7)Xd(FLsetyNtWwF^P}gU!JeUNGs!GShf^Gw{;y0M3Zdd!)ZbG$GA7YzIDHXl}uxW zfljM~uf~3*8{z&L?!aIhY%MEW=E*aXWoTBUyn<1k&N4o3B^AYO)b zO|n0Jcv=S*Xq+bh^bCZp`Sx>D^$ULUkrfm8Ugc`*)X_) zeD51?9@L^dfFXeY0m(J~+}LjR^&WcL6g)@_2+zaBv%*+^G_{EL$wLABKEI(t?~cbB zJ9IFI9p`P6#2fpi_;oxK2N<|eHDynJh`&XXnb?eW#s_O!j?s|>Out-;e-w;d`3Mtw z&q~ZxhH}4Cv-JbZ3(YCn=?dn<0!3%nCC9@3%21mOi-fdwzxau@+EeZ~9I$Z(3*MEV zsp5{o*l$nt1PcBbWjiQ+K-($ry|m5Lt~1QJf# zHc+=GN%)Z@K2_w;aZm`WRIsvb(@wNE<(h#kGP4P19~t~kRXDf zg>_>8q{rIFK+@lBjBRaldN^E&RzR1>{?mnq=;&Y7A-c2xE+RR|tdTRvLSSsgS2gg_j`I^H`G< zpc8gsL5?ylU{^YJ?X>!zX`Og=wBf`RUpaJb31S_!RsD=WTa^j% z)$q}&B>-tA>yjc5x_GAuxPs=URR&z~xe(gVT}2*uYaJO? zpNPhsDkqZ98UU|KOA?@?Uv-TtQxeR|RM-O3N=?M+(E0{=v{@2} z^YgQn_Ivxjx?{$eL*+fcFiVj^tEM;DX*>fe#QCW5=VvLXw-DnvxKG^pQ(c?6RzM<9 zQg^B{moscIm@V{zg1~?6>U8t zi4PN2s4_+A`Vm{1fn$&x`IF6HsZ(159n4BS43JMDeA4ICMcs z)+{&PdhHMP2z!Bn4hQ-NwQ`7$aQ!as%$rvhYo+{mQl+Nlb;tu?+^kbJi*;QXlJK(l zefK1j7PgoWVj!U}LDIWq-VJ|&-=|7_pm)eL4KG}ShjUEQa!~f0dt&6$MQM=u)?c;_ zcNG)ER!EFYEBo_vungUe7Crn2dZVFgBuiGsi7=Ea zJ=Cmhf8Ucuy?%M2ijOhc{>QRFxM~n&bdN}s2IY`M zg*$`@Sv=aXJZZ+Xv5ZTF&9l-I$_B8&G`XwG^UeCq6tkztIw~SK!0iL~$Y5#$np zP1p#=*2_iCXVqy-e(ZYyBY-wU?hrk`LHq}kN>0tm(5fu#f)D6;PKO9N{po*{bN1KP zxA&hj+WHf6!9ab!eUDU&I(kBWsu@8xK`%n>?-V0ItCRhLNVPkF1mosBx!hl9?J>VO z*fb;H$$o~RMnD0uK7Qn|oWH@$GmorG>!02<5O~4Kc1&rUO{TY3z&XEbN zu#XC8G44YEMwqwF92L%P+v9 z&J(bi;*(uA5#V^%E@O}q{u3b2b5gX`#(5^@dqoPAX>t}XFV59Y2#S`R0g6(9#>A2T zA$bo&otU`l3lj-|i1lU?;>6);)$T=3Au}#oTjO|LJEdZ{TbNe^x9bwEU%i+o3QX&Zwq4`lwLD8Ic zC*Bw6F^*X&di(1Y)dTSAaq1PgjNs^$oHSFM7;o-Yf$hcB3yPCL2Z!;$TvNMcZH`y~ zB0%p%SSN`2H=Sf5 zwp=P*FzDu*B7n1hpt{Mt(bcESGe(NihE+W6>WD1#f5u;(%*fqJ3!oze2T z5TkIDBxmDVIp@ShLkozHOXZvqiyZWbf7VGn#^f{rq}lccdk5cjmH3gpvxRgmjlskar5b%Xb%>cxJ z=V#<*n0f);o95#Ca8|3EH9Ybn{!Ss`z?31THPf(_%U-H20^?IR$hYns6la zymuIcaS@T`<`o5cK530+!W+bhBR2c|a@pGq(P0S4;#WU%7~6Y#o}b2bN5k4i@ATU8 z0*%ogZ)(?xi(_6u>2L+C-uiuzQvjXNvh5!qP=7njz^^{p8*d&|Almi2=xAt@qP6>< z!Ez(-wsNX1@6Ml7Z##g5p#+~An`^F+!vYg2*c*@l4w`tiAb|9aqD6wUCr+k@*E=?O z3?swcn2@qLD_tsq4L((0L(ym_WF45~LbzkaZGB=&}Pgwj=;xu@43(?E4+`_W}wqL6GM$*QJY- zJ}WoNoGF*}gHq)o$zCOn{51)vX(@r;PdgL3X#khENq`Ml4v^}(pHn5MzGY>JUN%42 zRc&7H^nU_Nu#Ag};5SsKOxk7mvkNA&WWS_qJ2r-v!mF~@7t6W>*tpvfo#u1Ibd8TI zIPh{ukLvFc{P|0tCkRDD@ZYxVRUl!N+&in&lC>w^wG6HnH(pzHD;6hfpK))WB= zg%ETs2&5_A!1e@qp7L^cML1hsV6#;94_k5;d5h->!1|0A9e_|XK()BYNDTz7)<)o2 z-Y$i!G1(tXPrB3_F<=dl_#+&qsnISc-H{BByak>BT|D0b6Nwkm=`@>2_lpa=VAOJD;^9AxA{?gQC`$X$N z8Xv_U9V)&RR(WZ1f_|}Hw4l+UKub9c>uP7mhNm&19E~i7vbfs)k-=cCVJYsDJ&Lcz ze>V(|90iIn+SK@6?s{6V$RWGDYH zY&)neD2SFKa`w6)tSvM2qa%`gp&TTHar-A0cW}fsE%X*(SCG6T7Q0aW8Jrm)#FsWK z*EXJqFU)oZxG~Ba zv+py7t1}<7`9cIN1@#AXoC|z|`hQJDzKRt-E5S6+oU_8kettojf~7m;^xv`dO|B^m0Mb)8cG za{Jyo^JlrwU|HzY9OKYE;tQ}9@IgmI6F^$vkI?So3w@&hI&Gbv!(U0@=?ZL1C&WjDrD-?zdG;B9fqZOUL|27zfG#*O;zIux7{f zPLc@3Gh-t&b-e)gc;=s8S0R3POf?N)EO3vomuPv!U-gd9%_ko{a#$G({s>^&W!z6X zH7bN#mqN)nuTM30nD+@d10$O@^Z5Xnh_VAzc00xFiH|mArYZRF{hie_##!te3L(Vj zwazw4+P&RlDOz$$Bxzz@lV^x?NNQkG0d{U(1m_)NlByN0S^#jHvr_cAkzCR?jP?!1 zB4TY~1sEtWQ`bl#08Pc^DM!_z)F_*Ae1Ml&c}$=B4=ZDBSRp54=vcW87bO|NSi7@LEc5q zM==owG4X;oOheg268FPUDo;DQtaG|X0N-5k?w1+jG?$Jh1yDH=aCSbbm1^fp4 zYHNpexde}bBECN@#{mJ5Fb?fQ0$AL*qEIQFR5wDQqD|G2(Tj{jByeMLjyGlz0++B{ zK#W_4LwAylKL#U(&-2k$CF+>ow1A!n!pcD)AlV^FSM|VK{h1t$N;!m3Hn~Kb;z|a@q$YtYB22v!{hah(iLCJ@$8OnNBp=QKi zu#W(cn3PKktG@+3VpN{HYx-TXoaswTmg!!ejd?*~1%dEWab4{7@nEPp`$RwvNIQUYWE6z~OP zl_;@6Mr@Q8vvi07KH4d$%@75qLNoYgyTs9SMev#f0V-V6HZ^%10l+VJbgOPHzNyZ} zI2HpCM4yI9i!d=^?aVbS=!i0ir@ev@+zZC-yDJ=L%YFp? z_Lk&;B2#H$pU%WUAb|O|E&I$DHdLCgp0H9i4VV@N+lXL>)*SSHnCe7zoVE(UkqH}F zgS2?{258jppBvhAdIfEiGfglcXh!P{pqu9mYi^As?&ttS$GJ=q~=UXG_iwJkwC+HCu`_+_Rx9BtM%m1O%Nh zhG-mbMw?ii+t~-ey;}AM_cE~*Ew}(Y5F$9!z5@EhW9U`_9IZ}EQAQbsIXXE!@5GGQ z7>(BuMMpoCn0wsgdp2h~e2WWw0?0o}a&_`-E8|hvZ-fv2NDTxOK*9*ho@i)S7SfH& z3$>4v#RTBFNgo&hLyj~$*Piq%@;PHkI6HJ53{=aV?&tUyUlGVmQ0T{cF$XyT1 zcd;1f9cBPkNUNrUdK&j2BKseZ0P|!?&S_hFzXYyZJNmU^9ph!18qf37YshLp9Hg=dB_WZ4Kzpj@eF}{u|yIlg>5KH+As8D2PunGluv`*2Q{KlIH_0O)8`<*tlVUJjd zyo#4^ls$6FvUJrCyzk&!L#0xpBQ=+JMDjV}J6OgL1YkdT<{S&PsCkIy2OL1cSdQn! zIfBfh!NW8n9!4mMw89#Ae?_6TwB1+LbllUi*__&2ElE584Qz{h8B;ya&rO1kPKcET zAa!yU)v$iuGoe)B?^PiY&Zod8mWTuy0H)qkvU&}&e+UV;zk%0XE2{@o z{9XwTKfbC|g&T3A1UfKlNNNC(Mgnwk03f{v6G8kRhn;N zB(Teli&ho@3Vvh-=ACW? z5YI`WgXuT4=yr_;7f?fIqyCV6Bb=?UlX6m(YQ3Xt@#uYF_*8&-02{jiBs>LrbF>;? zA$RRFEuH4FqFgg5_Xj>E)(e0{ONq83N?hWg-5=|h9mSq7Wg!LAip3}5PuoI(6}(st zfAea4uQIFOUtVCAOEU7uy4E@49hRQ`i-s4vkg$mm!}cYvM#ABA*^eYqWHc=!qvq}d zO=A#M zJ+%IxzARIP&>3_xHGRZ9)!1pCXlmD7s(mC*&VGRwQ3D?|@0am$L_e^P_idGTX$r(dB_oDt9#?PefR_vJDwHf}Sr$X>yD}XrL}8P&X=kh%%ol zi6K3RKa<2TSfGig5jc`$b9u9gRrD{6vE%u`vh>X^FQ=e$NVk(0-U?*LI zn97tybN9hURr~+cnsRg7x-xT*R1#qmdD|cS*=thmMAVgDXC{R`E9mx_+On0PQ-JKY zTydUUO4R{o)qjNM6x+34(`}juWf>h(Fl6b*9DV&Eku%e0kvp}&iLkR<@*d&_doCRuAhKmZ8ZL1F4OEHV-T zt!r176A`=}6{ai!IOB z*t<++e5|3}^o$?B!NYuvv-ivW4d(M3DpU`TO1%|!MQNTnPl8QGWfQCbJpQh>RY%`1 z2EXSrf~)sejh=||;NGsEl%#Z}-O-t=-mKytncndZt6M4=n$kYjI&&z&6 zwA$q(V7EM6+iI!LAi*d0W10}4<&zSRF@b0?vyMD%r}*)Uin7fsa;E(GRDee?2#LW- z@=lWb|4{*f)OScs^T{=(8pq-XUzFFT4bj~=&zDQ0PAlO3Rn6v4^7oS=@LV%vW8@6m zqhW@|8^XdxNwHFZOOSmWsr&ng#8lxr1R&hBNm5=dApVQ3J+>5#DbU;{1->sx@$jG& zTD?sb^v95kxPEzoDN9OF*7|D_OCOf^4T}e@n#H6I=7|4_&1(XR*m?Jbf>8{nTcbQB zL0Pl-O$zUPao?f^iv|&72J$biSO6X-;3CliHZkeL{^Vz15S=V}ZDLfE={3Ti%=L~1yea_*XfX?XTUiX z$QL8brQ)6$7j5$5;}j%gY88hX^d1y{u}yyB@VCly0KNXJg)AhhS*OeTGJOqZT%(r@ zsa*EU8Mzrc#l;1>@_v4}tJZW^OepvV?GlQmXYhv;q@DB(ac}^tjJW60J7Za<`OU#5 z+k5e0bo_L8aM&*R8qV4!0wVpvBfxXnC>OxUs7_izgMV{7N}9X!m}@WTZAA6->y{(Z~71rJuh=dY7QgfQg$J8KO2Kkd3- z5nulGH}*+oD>$Qj=VRv;WNG`_4jEkOC2{p}rQZmkU7j87a&W zI~|&wJ{l(zo1ty?iPdFC^~knK5$?`?b;doMg>m}cvV4{AxLv>urap#3n-k}lMA}N` zCGCA^M4gzGuKJzap$vn7;oTy+m;2|Y5DE~DY{lP7Z1oo@dQwQS`pNX^G|X!Al{L|}u6y)U@UM__I3J~O#{(^#lI4})~H;LI1|GvN4el1Ga zs}!@^hR1dI#qk3X&ukDcV3mV0xaURO%M_AY%)(85!^DJ%VKQ#S+wW(7Z?w1yfGmLM z53eZF`@bSNQ70iX^yJg)%9PRg-`ft@LaG6%8kE=dm$e#F0FujaU<6Z4hT?-URq9Fs zENI_4&_Ad&QO1~0x=oVdYI6ltVNOb9xO1#Aw6W;$2z{+$0V)a;W0jf*>ImXyH_OQv z*aJRLiU1=MmXVN|Bw?<*K-zpZUnZLf=qa!k|khCGgCqD2Gr^KTB7Pyj-P5jM6Z424{~-AquLpw)U>8scGQ<`?he!Y(eSUp~ zS&^3L9ID_Dvu4;!$Ebbx5V3y2E^76?LyJ@e3l;d5`5 zKy-y{I)t17$LunkKfwuK`<=gsXAbo>J0ZqoNYx6aMvfc|^qM#%3?ZQX4!tJEwKM7z z&6`ja#GbFw26>gNB>{D(1%mJdbYIw5VR}a=%ujaLy$xJrha+mqDBVBFzV;UE(T?`( z15FCnpwY9h_t4vz6I_1KZeiom$BptoV_Xr7=5n;dIMc8eV4MYW%3X-%(awpVXnvr@@wZY9FBgALYvk4C`R1jzZl%g&kztN~$5fmYN$@?VeI9W} zLcwZagSq&8TbCB|mMky^=s*m`7k4NHI*LCex5UV?Qq*|+ewcRd5|^rn_p zi&CI!Q2^ncwT|5H#NocQCBr&dFTltz_SWm2Nda`3+@COx(0?Pzo18wPtefvhY~VhK zhrHx60hnMJKGD=Eaam)?oEbpsv+^>`;}%R$-YhZcSpsNwNumDUsz$#-cy3cPPhrHr zD=*6y0110qz&m{DEpmT->&>d8{&qIwoPA|erRFva)NpB@^8DT>>w2l&8M|a3-6Lx| z=0~0hkV~PM4uJa(Hp`jrll@gFF+r+M3G1+_GdRpyfJSc?R@OTKFm@S$`MMcBixafuiXacmQx=H^p zQw0!`V=Bmq4S;LD0K$mGee2DGCYZ(=F*zk$0MJ`jmztl+v+ni)k+jvMy2&h)_lz*+ zH|0GO)bO*MwIA=UHGi{tg*kN@)7g5>VlvkHIthfSgj%KRbG#c@3GJjGUsI|RU4C_- zLHEdf3|0wj6EtwR!tmpP93rt0gZ*$_MS%`Ec%@@;?fbdx`Q|S36*<$iyYIL8KHV?D z;L#Y6B=LsjMdobz-n$Prndjubod}`v?yzcU zvuRmC0SOk@dHLDe_3%qh!jQ508G$q?If|IGl6%84jl~mPp>aGcORIWi3DQ> z5dVL-A3S<5AcH_lBGN19|2-lFNq>vuoJlwjv?G&0&`t)Bz?)OzS>A_v_Gyn+@ptmb(8AA3>3QPE`RE{@hq?){hI5S^k!q-g_Y%me)yE|%B zP~6jKOJn>iY^R6hvhJBfj|E0ll>|!AioGQoFvI|45dmZm(L#wZAk05IjFu@h1BOC? z4amA7PEHT<4V6XuOg#;H`yEnsgt3IC)Cwnc-PPLJ%JWK2G>ge%BOO`{)&0YMi-^9{ zjl1Q}ef8SX0G*v-2JS`-S4c3+d1`Wu^7Ib>8j{4&SYqfzPBt-6D`=t%TtonUsHgA zUGQJq4w#O28WHu|3B)@5RRNL`Lpy>cW6Orx@x&&Y(&!@A3 z9F@W@9Vn0@$RobJqsq3s6~YA~?i6uhpEQTS@Tg$efxaQ_mMN9}YlpF)C{Dx9c3(%< znD#^$7j)!^ql@aGTd`_VhxSa{7MIhy2m6fT@%TKz>_rJ6Eq2Rrwi0dc>DTt4af^_3 z7#--eP%#Y;d?9kloCc-5N`TB$mt~mw7>@ncwPlJ-bAizN?9G8(Ug8)VdXH4I_V>?S zA%|hKQv!V)Wma7D+y0&ljEcn0?g0g^_`QHsTrgG}uY0moSxA&Z#R#}b7~4ljb&P48 z7eD}gfFJIvRsinSHKh_nIsl{TX-9LwOcy=vgRu($80;|K&rQpVb-Gw6=`buNBiLht zAMLvz5HL3`#Mam@%NKzAOB*ZAQ<5Y-(b#d^wk~ax>Lf)TL|yGRT8$s5ZZ;(X+LNSq zk4~5?%a)m>sJB{?_;a2#8iL1KaGpQ@FE>8Myt>F(mrJ%}p0>MhK!wAJeY_dc5KN?d z1ieu?hcLteO8|5COYs`te`!gMwiBXd<|)gDuSYvbakN_A$?XE?81-ALNc@AwT zN{lxjl!E6gZN27+hW2?Squy+_6g!D=?F5i8*5fIOfrx`Xxw=eqsF{p78m1a-qYthq zGXHSG3cVw~zIDHOwWDW2OevUffA`8R^DnRORb3{SM|KpGD0-pOv^vevX4(7>o}B5J z0^hGJBs&Z-bmBz!>F(U;rz}g?bAy}?y3Q2qDX1@WFpED{C_at%(m2maflB~V5bTV+ zOm!4Y>ayY@VFTdMXs6&Zqav~@&HjLQ$!4~{wso0x5@4ixVY19-FhV{o!3H`8ckXY{ z=>Xmg)EIq^$0d1tRxUkXE*Zd$9ReURGSVAMjY`jylTCvNI^RPtH@(qTSKy&JHys*P zJ)uxCE)&KfL+}4UMWF_No<@rwmoDvoe<#7isX6Hi3g=2v?MZ*8@_3^JR{wk2TJv|C zR@lbo^4A!rdy7=t0I#Wt22+D-u^S9mT8*ese|SZ)iU}XDh8G}lVrtmKvTvAH77EjY z>@r3^lQ1H98y^605N$y8Lar*yGml9kmLQWG@}f~`yJwL+3_&LhPnT0kiJk%+-c zb!n_}Ms8bMt}SkN9cT#ILd}`E>84)dzMgUa+gK<>U09T3{@mDZ-t6wT%meX!uPM({ zzY7~PGR%G!C>mf)qqewFT7EV3);2gqcsTtI&2t*#8Vuv8&C@N()o=YP7cN z0hBUbao{)@da+R3-P13zdAw>N&1Gu^WWGrDJAiUb#Ox4Cg2dVQlf8X|swwL-v~91G z{KD04Oa~Q1z9{eq$~&+ZU|hbifQjKt08$VvFD_OEv#q*FN|F=I7dBLyms)!i<;2Zy z%1Sd!7CdVo8CNZ&cNZ=*{@ipkrw@D*r-Hp(<9gfMSj zS>lyBM6mD!?HM1{K;qL9B*saSx7R}}g*G3A71!=cY2Rhw$mtR*jZGp)(R_508;Sa7BsM@y`#~v6Jt=^2D8@S! z6xXDuDljsiw+A~YT7b1}o&HT6o6XSHLobRL8beq*EjBQPnO8e{l?i%*07!_PM|%`c znYQ9qlrR zjK3klXQ)h@d#4$$0#*GhrIL(Om}|NNJ_{zx$HY0k(%z$j*PdM=xHYEpz)(W$7_sNU z2|tDUx%Gz8O*I7dsPH%D7alZ~v&IPo{<7vyM5;L5Z_ zoz%2K5>SLL(Lo})0f0wC5fIO1fcyxrQdDRUz!XJ9LGk~0N)ojMKrh+xV#GtRzhMe{ zGD=_xkO)#V0x&3-yYyQ-s+9E)yEOD^@Zz4fY_l|a!*@*)aVPc?T0 zBwj>+i5PXhdR<3fX9++Gtg$y>UjQx)&lB7sF^IaCzk-EU>R-zkm0PnOr2;pTcAz5*r(T)sQ~;P`x?yX;bYeg77S?jWhHs$ z`Ib&?ZyTQH!3V?y_<_2L&>`=0Swft7x9lAf6y7US*O)9pJLH{v8_YfdMyPrNPNMfh z5(5yGD$bODFOi_@97%jY)6of<$q8h9fF&e4bRJcy8#D zxC=w_uLPA_I(v^kOLwE1Q6bf{;^agf#mV!7G5E5a%|Ob=M5Si|xfy1;B>phH1{Wz!98rD13xI7BjNeo@9Ra=wLHDh&zmp7qM$&kR6l?Hr zcG3O;s}R~fC_V-Mnk8Q^Nb-r_WJm&fQ&jx%%>clzBg}Nj6D3gH($%YA_iRSe2v7yR z9{=R8eif~nFfJKE4zqH}mh5;k61{T;v|w0IWFQL#{+z&G~0|m;1Eri;8oUsrW!uvw}9X zMgoXFEX8CBKp&Q(T(9h@yu>)2?8qQ6yDh>w_<~^yE{5NHEz&GmK#fohDl7;j8VCLu zfge@BcgY4Muza$qLsk1-qHO>b=}u~UrzE;d;8F06R>iaPGj(kqt@pm(B==}uAY=%2 z)>A}{HO29``mLGZF}wiO7+s|{3H;fEOnAUiX3Bn=nl?v&H?I$v^pTY%Dwz9vXPzG%6hE>t05B^ZCvf5aW;atvWV%F&GIQ(>c22Jn`8| zk`m1y>spRse(^nHGoWDrxm0^Ty{_C`S6Qgfno+m$@+<}el!%jvUM7hzRaN#+RsTDO zJrWFKKM?#GVFSYe7DmId1>QKH}pOLI!0h6|lXT77jj4J(8(H+`a@$K!^ z3M^Y~RE5!ENt3E1+GN*P6sq5)+Bq$W)qwbACb7X7rS)XM>90w^XkVM01C^%&N=t2J za*~OO3{$N;+IC=s`obh)Pu#q+$h;_^R{c^UvSi1h0TMjZ+^sB_U+k;%8mmmoSpk4$ z=t!?5Vt4GVGhaJpjp`h7Ms`Sn=9g0B>lh7PfE70QX*uZ{11`j`GmUIb$uf-t=hLEx zknkn)^9Ym!$f4_uqd3$#I;H?JT|Mr`lVgv^Nd9A-lhG)q@qOqPq6_(%_2uS2 z-#B18oNTq6*jV)^F#TbGos*ZP6aN58=3jAHk|yC&2M{b{sSd zwIv2p5R4@KWb9uSFS3e8CnaT_f zQAbS5*Q9e?Fb)uXrjsQoag4Bi>vxjyATZoAGOBd$#b)%1zbb(QwDs%cA|SwNm(Su0 z4#qj;dBgC+c{Ax`O8_o&g4?rlGgS;6+WqYk3@;YS_3>bw3tCP0$@zbeRHA4J1vKoM zqs=qeMoR`jFV!*j_)quLDid#s@NP`I1d2Q#s)+bbJI-*aRJx(k|7cgORJ{|-sW}$eN_W#>Eo7X0yD2l&@#D-R)%@}K~K`6LV=}N5)W}yoS zZrljs-zJDFH-eary3&=n5JeICK}f$yKPaT_lt7v(^!#o+DTLCNHeseP7Xob{i)7xr z_uO;OJI@#PyDbd+NI40tbKGzfBXU&O5Js;ttJd$x@gEGGQ5Zqm(8s*ezz!cyns%<3 zN42rp>ON_IlHjxmx9c%{MInk3m0U@#ZNbu%RGp6dQ{0$+PZJ^I$_#81mfzx^Jp;%=RH_M}_;Jj9fLN zd2Y=nM@$HtbKBo~GcX0;`&T{4?!_&7&BdL7-lMUB_JO&S1upBriZC za>H=}#e$eh7TA~=`24^g(ez2|{MY$aH6(s};j+jll(_MyU=cztAV;+Z-X#%-XzuLT zB&wu@zAE6}c?9I?&1T^B&& zxDCxM#iu;C)NYDcV|X(?5GgcrDQ)WsPauxd@nXI4Ki5n%b)&J$q*CUI^j%Z!@N6xr zrIMPIJ%v^LuCP66(7es4h%j+uuhMk!RR9seF-#HwXsOJI z<{;QY08EZ;a2p*m_EwkGlwSIxd#u|3WpTxTKk8L#22E6PIBQ4LXc8vtTXv*|0Ep7W zF&#gO1mdO_j#lznCZWIg{q7*ADo1#Xgx(|!HSp(aof8FV5=opUSly@*$cNO^&S5O1 z2`b{Ea60vW!Ee~UWWR!c@&o;DYTpVVZb7c+W?hDoG_+^k%6xn)2&`_5itwg~!7~qR zXnt+_o4oT-zaeHEO?XJFhIB-#XP<{;EuRkMZ_)WPf*tuRBP#7IF=T^KfU4G zFq{F~pw{n7quC}*Q3qEwbT5fuy3yZf%3cOG_@QC-@u5;POzJ;*>A|5SR4d*T78 r@1`_%y$SNwOV#jSB0@X8T2C9h?>IjaDm<&s00000NkvXXu0mjfVjdO& literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/img/icon-unknown.svg b/adaptive_hockey_federation/staticfiles/img/icon-unknown.svg new file mode 100644 index 00000000..50b4f972 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/img/icon-unknown.svg @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/staticfiles/img/logo.png b/adaptive_hockey_federation/staticfiles/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..714ea68a8f262685cfd952a569d40b8885b81548 GIT binary patch literal 42677 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 FV1o7&GG_n) literal 0 HcmV?d00001 diff --git a/adaptive_hockey_federation/staticfiles/img/rect.png b/adaptive_hockey_federation/staticfiles/img/rect.png new file mode 100644 index 0000000000000000000000000000000000000000..45f7789b691ac329e6edeb058143589670dec424 GIT binary patch literal 149035 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 + + diff --git a/adaptive_hockey_federation/staticfiles/img/selector-icons.svg b/adaptive_hockey_federation/staticfiles/img/selector-icons.svg new file mode 100644 index 00000000..926b8e21 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/img/selector-icons.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/adaptive_hockey_federation/staticfiles/js/bootstrap.bundle.min.js b/adaptive_hockey_federation/staticfiles/js/bootstrap.bundle.min.js new file mode 100644 index 00000000..d06052b7 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.0 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=N(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return M(s,{delegateTarget:r}),n.oneOff&&P.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return M(n,{delegateTarget:t}),i.oneOff&&P.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function I(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function N(t){return t=t.replace(y,""),T[t]||t}const P={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))I(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==N(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=M(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function M(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const H={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${F(e)}`))};class ${static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?H.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?H.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends ${constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),P.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.0"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return n(e)},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;P.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))};class q extends W{static get NAME(){return"alert"}close(){if(P.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),P.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(q,"close"),m(q);const V='[data-bs-toggle="button"]';class K extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=K.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}P.on(document,"click.bs.button.data-api",V,(t=>{t.preventDefault();const e=t.target.closest(V);K.getOrCreateInstance(e).toggle()})),m(K);const Q={endCallback:null,leftCallback:null,rightCallback:null},X={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Y extends ${constructor(t,e){super(),this._element=t,t&&Y.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Q}static get DefaultType(){return X}static get NAME(){return"swipe"}dispose(){P.off(this._element,".bs.swipe")}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(P.on(this._element,"pointerdown.bs.swipe",(t=>this._start(t))),P.on(this._element,"pointerup.bs.swipe",(t=>this._end(t))),this._element.classList.add("pointer-event")):(P.on(this._element,"touchstart.bs.swipe",(t=>this._start(t))),P.on(this._element,"touchmove.bs.swipe",(t=>this._move(t))),P.on(this._element,"touchend.bs.swipe",(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const U="next",G="prev",J="left",Z="right",tt="slid.bs.carousel",et="carousel",it="active",nt={ArrowLeft:Z,ArrowRight:J},st={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class rt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===et&&this.cycle()}static get Default(){return st}static get DefaultType(){return ot}static get NAME(){return"carousel"}next(){this._slide(U)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(G)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?P.one(this._element,tt,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void P.one(this._element,tt,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?U:G;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&P.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(P.on(this._element,"mouseenter.bs.carousel",(()=>this.pause())),P.on(this._element,"mouseleave.bs.carousel",(()=>this._maybeEnableCycle()))),this._config.touch&&Y.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))P.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(J)),rightCallback:()=>this._slide(this._directionToOrder(Z)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new Y(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=nt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(it),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===U,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>P.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r("slide.bs.carousel").defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(it),i.classList.remove(it,c,l),this._isSliding=!1,r(tt)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(".active.carousel-item",this._element)}_getItems(){return z.find(".carousel-item",this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===J?G:U:t===J?U:G}_orderToDirection(t){return p()?t===G?J:Z:t===G?Z:J}static jQueryInterface(t){return this.each((function(){const e=rt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}P.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(et))return;t.preventDefault();const i=rt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===H.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),P.on(window,"load.bs.carousel.data-api",(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)rt.getOrCreateInstance(e)})),m(rt);const at="show",lt="collapse",ct="collapsing",ht='[data-bs-toggle="collapse"]',dt={parent:null,toggle:!0},ut={parent:"(null|element)",toggle:"boolean"};class ft extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(ht);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return dt}static get DefaultType(){return ut}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>ft.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(P.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(lt),this._element.classList.add(ct),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ct),this._element.classList.add(lt,at),this._element.style[e]="",P.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(P.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(ct),this._element.classList.remove(lt,at);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ct),this._element.classList.add(lt),P.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(at)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(ht);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(":scope .collapse .collapse",this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=ft.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}P.on(document,"click.bs.collapse.data-api",ht,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))ft.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(ft);var pt="top",mt="bottom",gt="right",_t="left",bt="auto",vt=[pt,mt,gt,_t],yt="start",wt="end",At="clippingParents",Et="viewport",Tt="popper",Ct="reference",Ot=vt.reduce((function(t,e){return t.concat([e+"-"+yt,e+"-"+wt])}),[]),xt=[].concat(vt,[bt]).reduce((function(t,e){return t.concat([e,e+"-"+yt,e+"-"+wt])}),[]),kt="beforeRead",Lt="read",St="afterRead",Dt="beforeMain",It="main",Nt="afterMain",Pt="beforeWrite",Mt="write",jt="afterWrite",Ft=[kt,Lt,St,Dt,It,Nt,Pt,Mt,jt];function Ht(t){return t?(t.nodeName||"").toLowerCase():null}function $t(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function Wt(t){return t instanceof $t(t).Element||t instanceof Element}function Bt(t){return t instanceof $t(t).HTMLElement||t instanceof HTMLElement}function zt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof $t(t).ShadowRoot||t instanceof ShadowRoot)}const Rt={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];Bt(s)&&Ht(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});Bt(n)&&Ht(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function qt(t){return t.split("-")[0]}var Vt=Math.max,Kt=Math.min,Qt=Math.round;function Xt(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Yt(){return!/^((?!chrome|android).)*safari/i.test(Xt())}function Ut(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&Bt(t)&&(s=t.offsetWidth>0&&Qt(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Qt(n.height)/t.offsetHeight||1);var r=(Wt(t)?$t(t):window).visualViewport,a=!Yt()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Gt(t){var e=Ut(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Jt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&zt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Zt(t){return $t(t).getComputedStyle(t)}function te(t){return["table","td","th"].indexOf(Ht(t))>=0}function ee(t){return((Wt(t)?t.ownerDocument:t.document)||window.document).documentElement}function ie(t){return"html"===Ht(t)?t:t.assignedSlot||t.parentNode||(zt(t)?t.host:null)||ee(t)}function ne(t){return Bt(t)&&"fixed"!==Zt(t).position?t.offsetParent:null}function se(t){for(var e=$t(t),i=ne(t);i&&te(i)&&"static"===Zt(i).position;)i=ne(i);return i&&("html"===Ht(i)||"body"===Ht(i)&&"static"===Zt(i).position)?e:i||function(t){var e=/firefox/i.test(Xt());if(/Trident/i.test(Xt())&&Bt(t)&&"fixed"===Zt(t).position)return null;var i=ie(t);for(zt(i)&&(i=i.host);Bt(i)&&["html","body"].indexOf(Ht(i))<0;){var n=Zt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function oe(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function re(t,e,i){return Vt(t,Kt(e,i))}function ae(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function le(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const ce={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=qt(i.placement),l=oe(a),c=[_t,gt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return ae("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:le(t,vt))}(s.padding,i),d=Gt(o),u="y"===l?pt:_t,f="y"===l?mt:gt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=se(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=re(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Jt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function he(t){return t.split("-")[1]}var de={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ue(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=_t,y=pt,w=window;if(c){var A=se(i),E="clientHeight",T="clientWidth";A===$t(i)&&"static"!==Zt(A=ee(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===pt||(s===_t||s===gt)&&o===wt)&&(y=mt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==_t&&(s!==pt&&s!==mt||o!==wt)||(v=gt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&de),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:Qt(i*s)/s||0,y:Qt(n*s)/s||0}}({x:f,y:m},$t(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const fe={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:qt(e.placement),variation:he(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,ue(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,ue(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var pe={passive:!0};const me={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=$t(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,pe)})),a&&l.addEventListener("resize",i.update,pe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,pe)})),a&&l.removeEventListener("resize",i.update,pe)}},data:{}};var ge={left:"right",right:"left",bottom:"top",top:"bottom"};function _e(t){return t.replace(/left|right|bottom|top/g,(function(t){return ge[t]}))}var be={start:"end",end:"start"};function ve(t){return t.replace(/start|end/g,(function(t){return be[t]}))}function ye(t){var e=$t(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function we(t){return Ut(ee(t)).left+ye(t).scrollLeft}function Ae(t){var e=Zt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Ht(t))>=0?t.ownerDocument.body:Bt(t)&&Ae(t)?t:Ee(ie(t))}function Te(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=$t(n),r=s?[o].concat(o.visualViewport||[],Ae(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Te(ie(r)))}function Ce(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e,i){return e===Et?Ce(function(t,e){var i=$t(t),n=ee(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Yt();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+we(t),y:l}}(t,i)):Wt(e)?function(t,e){var i=Ut(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ce(function(t){var e,i=ee(t),n=ye(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=Vt(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=Vt(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+we(t),l=-n.scrollTop;return"rtl"===Zt(s||i).direction&&(a+=Vt(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(ee(t)))}function xe(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?qt(s):null,r=s?he(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case pt:e={x:a,y:i.y-n.height};break;case mt:e={x:a,y:i.y+i.height};break;case gt:e={x:i.x+i.width,y:l};break;case _t:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?oe(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case yt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case wt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?At:a,c=i.rootBoundary,h=void 0===c?Et:c,d=i.elementContext,u=void 0===d?Tt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=ae("number"!=typeof g?g:le(g,vt)),b=u===Tt?Ct:Tt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Te(ie(t)),i=["absolute","fixed"].indexOf(Zt(t).position)>=0&&Bt(t)?se(t):t;return Wt(i)?e.filter((function(t){return Wt(t)&&Jt(t,i)&&"body"!==Ht(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=Oe(t,i,n);return e.top=Vt(s.top,e.top),e.right=Kt(s.right,e.right),e.bottom=Kt(s.bottom,e.bottom),e.left=Vt(s.left,e.left),e}),Oe(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(Wt(y)?y:y.contextElement||ee(t.elements.popper),l,h,r),A=Ut(t.elements.reference),E=xe({reference:A,element:v,strategy:"absolute",placement:s}),T=Ce(Object.assign({},v,E)),C=u===Tt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Tt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[gt,mt].indexOf(t)>=0?1:-1,i=[pt,mt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?xt:l,h=he(n),d=h?a?Ot:Ot.filter((function(t){return he(t)===h})):vt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[qt(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const Se={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=qt(g),b=l||(_!==g&&p?function(t){if(qt(t)===bt)return[];var e=_e(t);return[ve(t),e,ve(e)]}(g):[_e(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(qt(i)===bt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ke(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),I=L?k?gt:_t:k?mt:pt;y[S]>w[S]&&(I=_e(I));var N=_e(I),P=[];if(o&&P.push(D[x]<=0),a&&P.push(D[I]<=0,D[N]<=0),P.every((function(t){return t}))){T=O,E=!1;break}A.set(O,P)}if(E)for(var M=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==M(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Ie(t){return[pt,gt,mt,_t].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Ie(l),d=Ie(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Pe={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=xt.reduce((function(t,i){return t[i]=function(t,e,i){var n=qt(t),s=[_t,pt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[_t,gt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},Me={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=xe({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},je={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=qt(e.placement),b=he(e.placement),v=!b,y=oe(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?pt:_t,D="y"===y?mt:gt,I="y"===y?"height":"width",N=A[y],P=N+g[S],M=N-g[D],j=f?-T[I]/2:0,F=b===yt?E[I]:T[I],H=b===yt?-T[I]:-E[I],$=e.elements.arrow,W=f&&$?Gt($):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=re(0,E[I],W[I]),V=v?E[I]/2-j-q-z-O.mainAxis:F-q-z-O.mainAxis,K=v?-E[I]/2+j+q+R+O.mainAxis:H+q+R+O.mainAxis,Q=e.elements.arrow&&se(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=N+K-Y,G=re(f?Kt(P,N+V-Y-X):P,N,f?Vt(M,U):M);A[y]=G,k[y]=G-N}if(a){var J,Z="x"===y?pt:_t,tt="x"===y?mt:gt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[pt,_t].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=re(t,e,i);return n>i?i:n}(at,et,lt):re(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function Fe(t,e,i){void 0===i&&(i=!1);var n,s,o=Bt(e),r=Bt(e)&&function(t){var e=t.getBoundingClientRect(),i=Qt(e.width)/t.offsetWidth||1,n=Qt(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=ee(e),l=Ut(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==Ht(e)||Ae(a))&&(c=(n=e)!==$t(n)&&Bt(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ye(n)),Bt(e)?((h=Ut(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=we(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var $e={placement:"bottom",modifiers:[],strategy:"absolute"};function We(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(H.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Xe,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=ci.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ze);for(const i of e){const e=ci.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Qe,Xe].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Je)?this:z.prev(this,Je)[0]||z.next(this,Je)[0]||z.findOne(Je,t.delegateTarget.parentNode),o=ci.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}P.on(document,Ue,Je,ci.dataApiKeydownHandler),P.on(document,Ue,ti,ci.dataApiKeydownHandler),P.on(document,Ye,ci.clearMenus),P.on(document,"keyup.bs.dropdown.data-api",ci.clearMenus),P.on(document,Ye,Je,(function(t){t.preventDefault(),ci.getOrCreateInstance(this).toggle()})),m(ci);const hi="show",di="mousedown.bs.backdrop",ui={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},fi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class pi extends ${constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return ui}static get DefaultType(){return fi}static get NAME(){return"backdrop"}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(hi),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(hi),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(P.off(this._element,di),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),P.on(t,di,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const mi=".bs.focustrap",gi="backward",_i={autofocus:!0,trapElement:null},bi={autofocus:"boolean",trapElement:"element"};class vi extends ${constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return _i}static get DefaultType(){return bi}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),P.off(document,mi),P.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),P.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,P.off(document,mi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===gi?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?gi:"forward")}}const yi=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",wi=".sticky-top",Ai="padding-right",Ei="margin-right";class Ti{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,Ai,(e=>e+t)),this._setElementAttributes(yi,Ai,(e=>e+t)),this._setElementAttributes(wi,Ei,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Ai),this._resetElementAttributes(yi,Ai),this._resetElementAttributes(wi,Ei)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&H.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=H.getDataAttribute(t,e);null!==i?(H.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const Ci=".bs.modal",Oi="hidden.bs.modal",xi="show.bs.modal",ki="modal-open",Li="show",Si="modal-static",Di={backdrop:!0,focus:!0,keyboard:!0},Ii={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ni extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new Ti,this._addEventListeners()}static get Default(){return Di}static get DefaultType(){return Ii}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||P.trigger(this._element,xi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(ki),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(P.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Li),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){P.off(window,Ci),P.off(this._dialog,Ci),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new pi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new vi({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(Li),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,P.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){P.on(this._element,"keydown.dismiss.bs.modal",(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),P.on(window,"resize.bs.modal",(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),P.on(this._element,"mousedown.dismiss.bs.modal",(t=>{P.one(this._element,"click.dismiss.bs.modal",(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(ki),this._resetAdjustments(),this._scrollBar.reset(),P.trigger(this._element,Oi)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(P.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(Si)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Si),this._queueCallback((()=>{this._element.classList.remove(Si),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ni.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}P.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),P.one(e,xi,(t=>{t.defaultPrevented||P.one(e,Oi,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&Ni.getInstance(i).hide(),Ni.getOrCreateInstance(e).toggle(this)})),R(Ni),m(Ni);const Pi="show",Mi="showing",ji="hiding",Fi=".offcanvas.show",Hi="hidePrevented.bs.offcanvas",$i="hidden.bs.offcanvas",Wi={backdrop:!0,keyboard:!0,scroll:!1},Bi={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class zi extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Wi}static get DefaultType(){return Bi}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||P.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new Ti).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Mi),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Pi),this._element.classList.remove(Mi),P.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(P.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ji),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Pi,ji),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new Ti).reset(),P.trigger(this._element,$i)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new pi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():P.trigger(this._element,Hi)}:null})}_initializeFocusTrap(){return new vi({trapElement:this._element})}_addEventListeners(){P.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():P.trigger(this._element,Hi))}))}static jQueryInterface(t){return this.each((function(){const e=zi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}P.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;P.one(e,$i,(()=>{a(this)&&this.focus()}));const i=z.findOne(Fi);i&&i!==e&&zi.getInstance(i).hide(),zi.getOrCreateInstance(e).toggle(this)})),P.on(window,"load.bs.offcanvas.data-api",(()=>{for(const t of z.find(Fi))zi.getOrCreateInstance(t).show()})),P.on(window,"resize.bs.offcanvas",(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&zi.getOrCreateInstance(t).hide()})),R(zi),m(zi);const Ri={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},qi=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Vi=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Ki=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!qi.has(i)||Boolean(Vi.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Qi={allowList:Ri,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"

"},Xi={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Yi={entry:"(string|element|function|null)",selector:"(string|element)"};class Ui extends ${constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Qi}static get DefaultType(){return Xi}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Yi)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Ki(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Gi=new Set(["sanitize","allowList","sanitizeFn"]),Ji="fade",Zi="show",tn=".modal",en="hide.bs.modal",nn="hover",sn="focus",on={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},rn={allowList:Ri,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},an={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class ln extends W{constructor(t,e){if(void 0===Ve)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return rn}static get DefaultType(){return an}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),P.off(this._element.closest(tn),en,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=P.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),P.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(Zi),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))P.on(t,"mouseover",h);this._queueCallback((()=>{P.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!P.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(Zi),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))P.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),P.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(Ji,Zi),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(Ji),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Ui({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Ji)}_isShown(){return this.tip&&this.tip.classList.contains(Zi)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=on[e.toUpperCase()];return qe(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)P.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===nn?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===nn?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");P.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?sn:nn]=!0,e._enter()})),P.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?sn:nn]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},P.on(this._element.closest(tn),en,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=H.getDataAttributes(this._element);for(const t of Object.keys(e))Gi.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(ln);const cn={...ln.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},hn={...ln.DefaultType,content:"(null|string|element|function)"};class dn extends ln{static get Default(){return cn}static get DefaultType(){return hn}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=dn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(dn);const un="click.bs.scrollspy",fn="active",pn="[href]",mn={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},gn={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class _n extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return mn}static get DefaultType(){return gn}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(P.off(this._config.target,un),P.on(this._config.target,un,pn,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(pn,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(fn),this._activateParents(t),P.trigger(this._element,"activate.bs.scrollspy",{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(fn);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,".nav-link, .nav-item > .nav-link, .list-group-item"))t.classList.add(fn)}_clearActiveClass(t){t.classList.remove(fn);const e=z.find("[href].active",t);for(const t of e)t.classList.remove(fn)}static jQueryInterface(t){return this.each((function(){const e=_n.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}P.on(window,"load.bs.scrollspy.data-api",(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))_n.getOrCreateInstance(t)})),m(_n);const bn="ArrowLeft",vn="ArrowRight",yn="ArrowUp",wn="ArrowDown",An="active",En="fade",Tn="show",Cn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',On=`.nav-link:not(.dropdown-toggle), .list-group-item:not(.dropdown-toggle), [role="tab"]:not(.dropdown-toggle), ${Cn}`;class xn extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),P.on(this._element,"keydown.bs.tab",(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?P.trigger(e,"hide.bs.tab",{relatedTarget:t}):null;P.trigger(t,"show.bs.tab",{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(An),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),P.trigger(t,"shown.bs.tab",{relatedTarget:e})):t.classList.add(Tn)}),t,t.classList.contains(En)))}_deactivate(t,e){t&&(t.classList.remove(An),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),P.trigger(t,"hidden.bs.tab",{relatedTarget:e})):t.classList.remove(Tn)}),t,t.classList.contains(En)))}_keydown(t){if(![bn,vn,yn,wn].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=[vn,wn].includes(t.key),i=b(this._getChildren().filter((t=>!l(t))),t.target,e,!0);i&&(i.focus({preventScroll:!0}),xn.getOrCreateInstance(i).show())}_getChildren(){return z.find(On,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(".dropdown-toggle",An),n(".dropdown-menu",Tn),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(An)}_getInnerElement(t){return t.matches(On)?t:z.findOne(On,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}P.on(document,"click.bs.tab",Cn,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||xn.getOrCreateInstance(this).show()})),P.on(window,"load.bs.tab",(()=>{for(const t of z.find('.active[data-bs-toggle="tab"], .active[data-bs-toggle="pill"], .active[data-bs-toggle="list"]'))xn.getOrCreateInstance(t)})),m(xn);const kn="hide",Ln="show",Sn="showing",Dn={animation:"boolean",autohide:"boolean",delay:"number"},In={animation:!0,autohide:!0,delay:5e3};class Nn extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return In}static get DefaultType(){return Dn}static get NAME(){return"toast"}show(){P.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(kn),d(this._element),this._element.classList.add(Ln,Sn),this._queueCallback((()=>{this._element.classList.remove(Sn),P.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(P.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(Sn),this._queueCallback((()=>{this._element.classList.add(kn),this._element.classList.remove(Sn,Ln),P.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(Ln),super.dispose()}isShown(){return this._element.classList.contains(Ln)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){P.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),P.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),P.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),P.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Nn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Nn),m(Nn),{Alert:q,Button:K,Carousel:rt,Collapse:ft,Dropdown:ci,Modal:Ni,Offcanvas:zi,Popover:dn,ScrollSpy:_n,Tab:xn,Toast:Nn,Tooltip:ln}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map diff --git a/adaptive_hockey_federation/staticfiles/js/date-picker.js b/adaptive_hockey_federation/staticfiles/js/date-picker.js new file mode 100644 index 00000000..53c95a97 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/js/date-picker.js @@ -0,0 +1,43 @@ +class DatePicker { + constructor(yearSelect, monthSelect, daySelect) { + this.yearSelect = yearSelect + this.monthSelect = monthSelect + this.daySelect = daySelect + this._init() + } + + _init() { + this._populateSelect(this.yearSelect, this._getYears(), 'гггг') + this._populateSelect(this.monthSelect, this._getMonths(), 'мм') + this._populateSelect(this.daySelect, this._getDays(), 'дд') + } + + _getYears() { + const currentYear = new Date().getFullYear() + return Array.from({ length: currentYear - 1999 }, (_, i) => currentYear - i) + } + + _getMonths() { + return Array.from({ length: 12 }, (_, i) => (i + 1).toString().padStart(2, '0')) + } + + _getDays() { + return Array.from({ length: 31 }, (_, i) => (i + 1).toString().padStart(2, '0')) + } + + _populateSelect(selectElement, optionsArray, defaultValue) { + selectElement.innerHTML = '' + const defaultOption = document.createElement('option') + defaultOption.value = '' + defaultOption.selected = true + defaultOption.textContent = defaultValue + selectElement.appendChild(defaultOption) + + optionsArray.forEach(option => { + const optionElement = document.createElement('option') + optionElement.value = option + optionElement.textContent = option + selectElement.appendChild(optionElement) + }) + } +} diff --git a/adaptive_hockey_federation/staticfiles/js/discipline-picker.js b/adaptive_hockey_federation/staticfiles/js/discipline-picker.js new file mode 100644 index 00000000..bf65c9aa --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/js/discipline-picker.js @@ -0,0 +1,45 @@ +class DisciplineNamePicker { + constructor(selectActive, optionsArray) { + this.selectActive = selectActive; + this.optionsArray = optionsArray; + this._init() + } + + _init() { + this._getDiscipline().then(data => { + const disciplineNames = data.map(discipline => discipline.name); + this._populateSelect(this.selectActive, disciplineNames, '----'); + }); + } + + _getDiscipline() { + return new Promise((resolve, reject) => { + $.ajax({ + url: '/ajax/filter-discipline-search/', + type: 'get', + success: function(data) { + resolve(data); + }, + error: function(error) { + reject(error); + } + }); + }); + } + + _populateSelect(selectElement, optionsArray, defaultValue) { + selectElement.innerHTML = ''; + const defaultOption = document.createElement('option'); + defaultOption.value = ''; + defaultOption.selected = true; + defaultOption.textContent = defaultValue; + selectElement.appendChild(defaultOption); + + optionsArray.forEach(option => { + const optionElement = document.createElement('option'); + optionElement.value = option; + optionElement.textContent = option; + selectElement.appendChild(optionElement); + }); + } +} diff --git a/adaptive_hockey_federation/staticfiles/js/jquery-select2.min.js b/adaptive_hockey_federation/staticfiles/js/jquery-select2.min.js new file mode 100644 index 00000000..98c4d228 --- /dev/null +++ b/adaptive_hockey_federation/staticfiles/js/jquery-select2.min.js @@ -0,0 +1,3182 @@ +! function(e, t) { + "use strict"; + "object" == typeof module && "object" == typeof module.exports ? module.exports = e.document ? t(e, !0) : function(e) { + if (!e.document) throw new Error("jQuery requires a window with a document"); + return t(e) + } : t(e) +}("undefined" != typeof window ? window : this, function(ie, e) { + "use strict"; + var oe = [], + r = Object.getPrototypeOf, + ae = oe.slice, + g = oe.flat ? function(e) { + return oe.flat.call(e) + } : function(e) { + return oe.concat.apply([], e) + }, + s = oe.push, + se = oe.indexOf, + n = {}, + i = n.toString, + ue = n.hasOwnProperty, + o = ue.toString, + a = o.call(Object), + le = {}, + v = function(e) { + return "function" == typeof e && "number" != typeof e.nodeType && "function" != typeof e.item + }, + y = function(e) { + return null != e && e === e.window + }, + C = ie.document, + u = { + type: !0, + src: !0, + nonce: !0, + noModule: !0 + }; + + function m(e, t, n) { + var r, i, o = (n = n || C).createElement("script"); + if (o.text = e, t) + for (r in u)(i = t[r] || t.getAttribute && t.getAttribute(r)) && o.setAttribute(r, i); + n.head.appendChild(o).parentNode.removeChild(o) + } + + function x(e) { + return null == e ? e + "" : "object" == typeof e || "function" == typeof e ? n[i.call(e)] || "object" : typeof e + } + var t = "3.7.1", + l = /HTML$/i, + ce = function(e, t) { + return new ce.fn.init(e, t) + }; + + function c(e) { + var t = !!e && "length" in e && e.length, + n = x(e); + return !v(e) && !y(e) && ("array" === n || 0 === t || "number" == typeof t && 0 < t && t - 1 in e) + } + + function fe(e, t) { + return e.nodeName && e.nodeName.toLowerCase() === t.toLowerCase() + } + ce.fn = ce.prototype = { + jquery: t, + constructor: ce, + length: 0, + toArray: function() { + return ae.call(this) + }, + get: function(e) { + return null == e ? ae.call(this) : e < 0 ? this[e + this.length] : this[e] + }, + pushStack: function(e) { + var t = ce.merge(this.constructor(), e); + return t.prevObject = this, t + }, + each: function(e) { + return ce.each(this, e) + }, + map: function(n) { + return this.pushStack(ce.map(this, function(e, t) { + return n.call(e, t, e) + })) + }, + slice: function() { + return this.pushStack(ae.apply(this, arguments)) + }, + first: function() { + return this.eq(0) + }, + last: function() { + return this.eq(-1) + }, + even: function() { + return this.pushStack(ce.grep(this, function(e, t) { + return (t + 1) % 2 + })) + }, + odd: function() { + return this.pushStack(ce.grep(this, function(e, t) { + return t % 2 + })) + }, + eq: function(e) { + var t = this.length, + n = +e + (e < 0 ? t : 0); + return this.pushStack(0 <= n && n < t ? [this[n]] : []) + }, + end: function() { + return this.prevObject || this.constructor() + }, + push: s, + sort: oe.sort, + splice: oe.splice + }, ce.extend = ce.fn.extend = function() { + var e, t, n, r, i, o, a = arguments[0] || {}, + s = 1, + u = arguments.length, + l = !1; + for ("boolean" == typeof a && (l = a, a = arguments[s] || {}, s++), "object" == typeof a || v(a) || (a = {}), s === u && (a = this, s--); s < u; s++) + if (null != (e = arguments[s])) + for (t in e) r = e[t], "__proto__" !== t && a !== r && (l && r && (ce.isPlainObject(r) || (i = Array.isArray(r))) ? (n = a[t], o = i && !Array.isArray(n) ? [] : i || ce.isPlainObject(n) ? n : {}, i = !1, a[t] = ce.extend(l, o, r)) : void 0 !== r && (a[t] = r)); + return a + }, ce.extend({ + expando: "jQuery" + (t + Math.random()).replace(/\D/g, ""), + isReady: !0, + error: function(e) { + throw new Error(e) + }, + noop: function() {}, + isPlainObject: function(e) { + var t, n; + return !(!e || "[object Object]" !== i.call(e)) && (!(t = r(e)) || "function" == typeof(n = ue.call(t, "constructor") && t.constructor) && o.call(n) === a) + }, + isEmptyObject: function(e) { + var t; + for (t in e) return !1; + return !0 + }, + globalEval: function(e, t, n) { + m(e, { + nonce: t && t.nonce + }, n) + }, + each: function(e, t) { + var n, r = 0; + if (c(e)) { + for (n = e.length; r < n; r++) + if (!1 === t.call(e[r], r, e[r])) break + } else + for (r in e) + if (!1 === t.call(e[r], r, e[r])) break; + return e + }, + text: function(e) { + var t, n = "", + r = 0, + i = e.nodeType; + if (!i) + while (t = e[r++]) n += ce.text(t); + return 1 === i || 11 === i ? e.textContent : 9 === i ? e.documentElement.textContent : 3 === i || 4 === i ? e.nodeValue : n + }, + makeArray: function(e, t) { + var n = t || []; + return null != e && (c(Object(e)) ? ce.merge(n, "string" == typeof e ? [e] : e) : s.call(n, e)), n + }, + inArray: function(e, t, n) { + return null == t ? -1 : se.call(t, e, n) + }, + isXMLDoc: function(e) { + var t = e && e.namespaceURI, + n = e && (e.ownerDocument || e).documentElement; + return !l.test(t || n && n.nodeName || "HTML") + }, + merge: function(e, t) { + for (var n = +t.length, r = 0, i = e.length; r < n; r++) e[i++] = t[r]; + return e.length = i, e + }, + grep: function(e, t, n) { + for (var r = [], i = 0, o = e.length, a = !n; i < o; i++) !t(e[i], i) !== a && r.push(e[i]); + return r + }, + map: function(e, t, n) { + var r, i, o = 0, + a = []; + if (c(e)) + for (r = e.length; o < r; o++) null != (i = t(e[o], o, n)) && a.push(i); + else + for (o in e) null != (i = t(e[o], o, n)) && a.push(i); + return g(a) + }, + guid: 1, + support: le + }), "function" == typeof Symbol && (ce.fn[Symbol.iterator] = oe[Symbol.iterator]), ce.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function(e, t) { + n["[object " + t + "]"] = t.toLowerCase() + }); + var pe = oe.pop, + de = oe.sort, + he = oe.splice, + ge = "[\\x20\\t\\r\\n\\f]", + ve = new RegExp("^" + ge + "+|((?:^|[^\\\\])(?:\\\\.)*)" + ge + "+$", "g"); + ce.contains = function(e, t) { + var n = t && t.parentNode; + return e === n || !(!n || 1 !== n.nodeType || !(e.contains ? e.contains(n) : e.compareDocumentPosition && 16 & e.compareDocumentPosition(n))) + }; + var f = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + + function p(e, t) { + return t ? "\0" === e ? "\ufffd" : e.slice(0, -1) + "\\" + e.charCodeAt(e.length - 1).toString(16) + " " : "\\" + e + } + ce.escapeSelector = function(e) { + return (e + "").replace(f, p) + }; + var ye = C, + me = s; + ! function() { + var e, b, w, o, a, T, r, C, d, i, k = me, + S = ce.expando, + E = 0, + n = 0, + s = W(), + c = W(), + u = W(), + h = W(), + l = function(e, t) { + return e === t && (a = !0), 0 + }, + f = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + t = "(?:\\\\[\\da-fA-F]{1,6}" + ge + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + p = "\\[" + ge + "*(" + t + ")(?:" + ge + "*([*^$|!~]?=)" + ge + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + t + "))|)" + ge + "*\\]", + g = ":(" + t + ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" + p + ")*)|.*)\\)|)", + v = new RegExp(ge + "+", "g"), + y = new RegExp("^" + ge + "*," + ge + "*"), + m = new RegExp("^" + ge + "*([>+~]|" + ge + ")" + ge + "*"), + x = new RegExp(ge + "|>"), + j = new RegExp(g), + A = new RegExp("^" + t + "$"), + D = { + ID: new RegExp("^#(" + t + ")"), + CLASS: new RegExp("^\\.(" + t + ")"), + TAG: new RegExp("^(" + t + "|[*])"), + ATTR: new RegExp("^" + p), + PSEUDO: new RegExp("^" + g), + CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + ge + "*(even|odd|(([+-]|)(\\d*)n|)" + ge + "*(?:([+-]|)" + ge + "*(\\d+)|))" + ge + "*\\)|)", "i"), + bool: new RegExp("^(?:" + f + ")$", "i"), + needsContext: new RegExp("^" + ge + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + ge + "*((?:-\\d)?\\d*)" + ge + "*\\)|)(?=[^-]|$)", "i") + }, + N = /^(?:input|select|textarea|button)$/i, + q = /^h\d$/i, + L = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + H = /[+~]/, + O = new RegExp("\\\\[\\da-fA-F]{1,6}" + ge + "?|\\\\([^\\r\\n\\f])", "g"), + P = function(e, t) { + var n = "0x" + e.slice(1) - 65536; + return t || (n < 0 ? String.fromCharCode(n + 65536) : String.fromCharCode(n >> 10 | 55296, 1023 & n | 56320)) + }, + M = function() { + V() + }, + R = J(function(e) { + return !0 === e.disabled && fe(e, "fieldset") + }, { + dir: "parentNode", + next: "legend" + }); + try { + k.apply(oe = ae.call(ye.childNodes), ye.childNodes), oe[ye.childNodes.length].nodeType + } catch (e) { + k = { + apply: function(e, t) { + me.apply(e, ae.call(t)) + }, + call: function(e) { + me.apply(e, ae.call(arguments, 1)) + } + } + } + + function I(t, e, n, r) { + var i, o, a, s, u, l, c, f = e && e.ownerDocument, + p = e ? e.nodeType : 9; + if (n = n || [], "string" != typeof t || !t || 1 !== p && 9 !== p && 11 !== p) return n; + if (!r && (V(e), e = e || T, C)) { + if (11 !== p && (u = L.exec(t))) + if (i = u[1]) { + if (9 === p) { + if (!(a = e.getElementById(i))) return n; + if (a.id === i) return k.call(n, a), n + } else if (f && (a = f.getElementById(i)) && I.contains(e, a) && a.id === i) return k.call(n, a), n + } else { + if (u[2]) return k.apply(n, e.getElementsByTagName(t)), n; + if ((i = u[3]) && e.getElementsByClassName) return k.apply(n, e.getElementsByClassName(i)), n + } if (!(h[t + " "] || d && d.test(t))) { + if (c = t, f = e, 1 === p && (x.test(t) || m.test(t))) { + (f = H.test(t) && U(e.parentNode) || e) == e && le.scope || ((s = e.getAttribute("id")) ? s = ce.escapeSelector(s) : e.setAttribute("id", s = S)), o = (l = Y(t)).length; + while (o--) l[o] = (s ? "#" + s : ":scope") + " " + Q(l[o]); + c = l.join(",") + } + try { + return k.apply(n, f.querySelectorAll(c)), n + } catch (e) { + h(t, !0) + } finally { + s === S && e.removeAttribute("id") + } + } + } + return re(t.replace(ve, "$1"), e, n, r) + } + + function W() { + var r = []; + return function e(t, n) { + return r.push(t + " ") > b.cacheLength && delete e[r.shift()], e[t + " "] = n + } + } + + function F(e) { + return e[S] = !0, e + } + + function $(e) { + var t = T.createElement("fieldset"); + try { + return !!e(t) + } catch (e) { + return !1 + } finally { + t.parentNode && t.parentNode.removeChild(t), t = null + } + } + + function B(t) { + return function(e) { + return fe(e, "input") && e.type === t + } + } + + function _(t) { + return function(e) { + return (fe(e, "input") || fe(e, "button")) && e.type === t + } + } + + function z(t) { + return function(e) { + return "form" in e ? e.parentNode && !1 === e.disabled ? "label" in e ? "label" in e.parentNode ? e.parentNode.disabled === t : e.disabled === t : e.isDisabled === t || e.isDisabled !== !t && R(e) === t : e.disabled === t : "label" in e && e.disabled === t + } + } + + function X(a) { + return F(function(o) { + return o = +o, F(function(e, t) { + var n, r = a([], e.length, o), + i = r.length; + while (i--) e[n = r[i]] && (e[n] = !(t[n] = e[n])) + }) + }) + } + + function U(e) { + return e && "undefined" != typeof e.getElementsByTagName && e + } + + function V(e) { + var t, n = e ? e.ownerDocument || e : ye; + return n != T && 9 === n.nodeType && n.documentElement && (r = (T = n).documentElement, C = !ce.isXMLDoc(T), i = r.matches || r.webkitMatchesSelector || r.msMatchesSelector, r.msMatchesSelector && ye != T && (t = T.defaultView) && t.top !== t && t.addEventListener("unload", M), le.getById = $(function(e) { + return r.appendChild(e).id = ce.expando, !T.getElementsByName || !T.getElementsByName(ce.expando).length + }), le.disconnectedMatch = $(function(e) { + return i.call(e, "*") + }), le.scope = $(function() { + return T.querySelectorAll(":scope") + }), le.cssHas = $(function() { + try { + return T.querySelector(":has(*,:jqfake)"), !1 + } catch (e) { + return !0 + } + }), le.getById ? (b.filter.ID = function(e) { + var t = e.replace(O, P); + return function(e) { + return e.getAttribute("id") === t + } + }, b.find.ID = function(e, t) { + if ("undefined" != typeof t.getElementById && C) { + var n = t.getElementById(e); + return n ? [n] : [] + } + }) : (b.filter.ID = function(e) { + var n = e.replace(O, P); + return function(e) { + var t = "undefined" != typeof e.getAttributeNode && e.getAttributeNode("id"); + return t && t.value === n + } + }, b.find.ID = function(e, t) { + if ("undefined" != typeof t.getElementById && C) { + var n, r, i, o = t.getElementById(e); + if (o) { + if ((n = o.getAttributeNode("id")) && n.value === e) return [o]; + i = t.getElementsByName(e), r = 0; + while (o = i[r++]) + if ((n = o.getAttributeNode("id")) && n.value === e) return [o] + } + return [] + } + }), b.find.TAG = function(e, t) { + return "undefined" != typeof t.getElementsByTagName ? t.getElementsByTagName(e) : t.querySelectorAll(e) + }, b.find.CLASS = function(e, t) { + if ("undefined" != typeof t.getElementsByClassName && C) return t.getElementsByClassName(e) + }, d = [], $(function(e) { + var t; + r.appendChild(e).innerHTML = "", e.querySelectorAll("[selected]").length || d.push("\\[" + ge + "*(?:value|" + f + ")"), e.querySelectorAll("[id~=" + S + "-]").length || d.push("~="), e.querySelectorAll("a#" + S + "+*").length || d.push(".#.+[+~]"), e.querySelectorAll(":checked").length || d.push(":checked"), (t = T.createElement("input")).setAttribute("type", "hidden"), e.appendChild(t).setAttribute("name", "D"), r.appendChild(e).disabled = !0, 2 !== e.querySelectorAll(":disabled").length && d.push(":enabled", ":disabled"), (t = T.createElement("input")).setAttribute("name", ""), e.appendChild(t), e.querySelectorAll("[name='']").length || d.push("\\[" + ge + "*name" + ge + "*=" + ge + "*(?:''|\"\")") + }), le.cssHas || d.push(":has"), d = d.length && new RegExp(d.join("|")), l = function(e, t) { + if (e === t) return a = !0, 0; + var n = !e.compareDocumentPosition - !t.compareDocumentPosition; + return n || (1 & (n = (e.ownerDocument || e) == (t.ownerDocument || t) ? e.compareDocumentPosition(t) : 1) || !le.sortDetached && t.compareDocumentPosition(e) === n ? e === T || e.ownerDocument == ye && I.contains(ye, e) ? -1 : t === T || t.ownerDocument == ye && I.contains(ye, t) ? 1 : o ? se.call(o, e) - se.call(o, t) : 0 : 4 & n ? -1 : 1) + }), T + } + for (e in I.matches = function(e, t) { + return I(e, null, null, t) + }, I.matchesSelector = function(e, t) { + if (V(e), C && !h[t + " "] && (!d || !d.test(t))) try { + var n = i.call(e, t); + if (n || le.disconnectedMatch || e.document && 11 !== e.document.nodeType) return n + } catch (e) { + h(t, !0) + } + return 0 < I(t, T, null, [e]).length + }, I.contains = function(e, t) { + return (e.ownerDocument || e) != T && V(e), ce.contains(e, t) + }, I.attr = function(e, t) { + (e.ownerDocument || e) != T && V(e); + var n = b.attrHandle[t.toLowerCase()], + r = n && ue.call(b.attrHandle, t.toLowerCase()) ? n(e, t, !C) : void 0; + return void 0 !== r ? r : e.getAttribute(t) + }, I.error = function(e) { + throw new Error("Syntax error, unrecognized expression: " + e) + }, ce.uniqueSort = function(e) { + var t, n = [], + r = 0, + i = 0; + if (a = !le.sortStable, o = !le.sortStable && ae.call(e, 0), de.call(e, l), a) { + while (t = e[i++]) t === e[i] && (r = n.push(i)); + while (r--) he.call(e, n[r], 1) + } + return o = null, e + }, ce.fn.uniqueSort = function() { + return this.pushStack(ce.uniqueSort(ae.apply(this))) + }, (b = ce.expr = { + cacheLength: 50, + createPseudo: F, + match: D, + attrHandle: {}, + find: {}, + relative: { + ">": { + dir: "parentNode", + first: !0 + }, + " ": { + dir: "parentNode" + }, + "+": { + dir: "previousSibling", + first: !0 + }, + "~": { + dir: "previousSibling" + } + }, + preFilter: { + ATTR: function(e) { + return e[1] = e[1].replace(O, P), e[3] = (e[3] || e[4] || e[5] || "").replace(O, P), "~=" === e[2] && (e[3] = " " + e[3] + " "), e.slice(0, 4) + }, + CHILD: function(e) { + return e[1] = e[1].toLowerCase(), "nth" === e[1].slice(0, 3) ? (e[3] || I.error(e[0]), e[4] = +(e[4] ? e[5] + (e[6] || 1) : 2 * ("even" === e[3] || "odd" === e[3])), e[5] = +(e[7] + e[8] || "odd" === e[3])) : e[3] && I.error(e[0]), e + }, + PSEUDO: function(e) { + var t, n = !e[6] && e[2]; + return D.CHILD.test(e[0]) ? null : (e[3] ? e[2] = e[4] || e[5] || "" : n && j.test(n) && (t = Y(n, !0)) && (t = n.indexOf(")", n.length - t) - n.length) && (e[0] = e[0].slice(0, t), e[2] = n.slice(0, t)), e.slice(0, 3)) + } + }, + filter: { + TAG: function(e) { + var t = e.replace(O, P).toLowerCase(); + return "*" === e ? function() { + return !0 + } : function(e) { + return fe(e, t) + } + }, + CLASS: function(e) { + var t = s[e + " "]; + return t || (t = new RegExp("(^|" + ge + ")" + e + "(" + ge + "|$)")) && s(e, function(e) { + return t.test("string" == typeof e.className && e.className || "undefined" != typeof e.getAttribute && e.getAttribute("class") || "") + }) + }, + ATTR: function(n, r, i) { + return function(e) { + var t = I.attr(e, n); + return null == t ? "!=" === r : !r || (t += "", "=" === r ? t === i : "!=" === r ? t !== i : "^=" === r ? i && 0 === t.indexOf(i) : "*=" === r ? i && -1 < t.indexOf(i) : "$=" === r ? i && t.slice(-i.length) === i : "~=" === r ? -1 < (" " + t.replace(v, " ") + " ").indexOf(i) : "|=" === r && (t === i || t.slice(0, i.length + 1) === i + "-")) + } + }, + CHILD: function(d, e, t, h, g) { + var v = "nth" !== d.slice(0, 3), + y = "last" !== d.slice(-4), + m = "of-type" === e; + return 1 === h && 0 === g ? function(e) { + return !!e.parentNode + } : function(e, t, n) { + var r, i, o, a, s, u = v !== y ? "nextSibling" : "previousSibling", + l = e.parentNode, + c = m && e.nodeName.toLowerCase(), + f = !n && !m, + p = !1; + if (l) { + if (v) { + while (u) { + o = e; + while (o = o[u]) + if (m ? fe(o, c) : 1 === o.nodeType) return !1; + s = u = "only" === d && !s && "nextSibling" + } + return !0 + } + if (s = [y ? l.firstChild : l.lastChild], y && f) { + p = (a = (r = (i = l[S] || (l[S] = {}))[d] || [])[0] === E && r[1]) && r[2], o = a && l.childNodes[a]; + while (o = ++a && o && o[u] || (p = a = 0) || s.pop()) + if (1 === o.nodeType && ++p && o === e) { + i[d] = [E, a, p]; + break + } + } else if (f && (p = a = (r = (i = e[S] || (e[S] = {}))[d] || [])[0] === E && r[1]), !1 === p) + while (o = ++a && o && o[u] || (p = a = 0) || s.pop()) + if ((m ? fe(o, c) : 1 === o.nodeType) && ++p && (f && ((i = o[S] || (o[S] = {}))[d] = [E, p]), o === e)) break; + return (p -= g) === h || p % h == 0 && 0 <= p / h + } + } + }, + PSEUDO: function(e, o) { + var t, a = b.pseudos[e] || b.setFilters[e.toLowerCase()] || I.error("unsupported pseudo: " + e); + return a[S] ? a(o) : 1 < a.length ? (t = [e, e, "", o], b.setFilters.hasOwnProperty(e.toLowerCase()) ? F(function(e, t) { + var n, r = a(e, o), + i = r.length; + while (i--) e[n = se.call(e, r[i])] = !(t[n] = r[i]) + }) : function(e) { + return a(e, 0, t) + }) : a + } + }, + pseudos: { + not: F(function(e) { + var r = [], + i = [], + s = ne(e.replace(ve, "$1")); + return s[S] ? F(function(e, t, n, r) { + var i, o = s(e, null, r, []), + a = e.length; + while (a--)(i = o[a]) && (e[a] = !(t[a] = i)) + }) : function(e, t, n) { + return r[0] = e, s(r, null, n, i), r[0] = null, !i.pop() + } + }), + has: F(function(t) { + return function(e) { + return 0 < I(t, e).length + } + }), + contains: F(function(t) { + return t = t.replace(O, P), + function(e) { + return -1 < (e.textContent || ce.text(e)).indexOf(t) + } + }), + lang: F(function(n) { + return A.test(n || "") || I.error("unsupported lang: " + n), n = n.replace(O, P).toLowerCase(), + function(e) { + var t; + do { + if (t = C ? e.lang : e.getAttribute("xml:lang") || e.getAttribute("lang")) return (t = t.toLowerCase()) === n || 0 === t.indexOf(n + "-") + } while ((e = e.parentNode) && 1 === e.nodeType); + return !1 + } + }), + target: function(e) { + var t = ie.location && ie.location.hash; + return t && t.slice(1) === e.id + }, + root: function(e) { + return e === r + }, + focus: function(e) { + return e === function() { + try { + return T.activeElement + } catch (e) {} + }() && T.hasFocus() && !!(e.type || e.href || ~e.tabIndex) + }, + enabled: z(!1), + disabled: z(!0), + checked: function(e) { + return fe(e, "input") && !!e.checked || fe(e, "option") && !!e.selected + }, + selected: function(e) { + return e.parentNode && e.parentNode.selectedIndex, !0 === e.selected + }, + empty: function(e) { + for (e = e.firstChild; e; e = e.nextSibling) + if (e.nodeType < 6) return !1; + return !0 + }, + parent: function(e) { + return !b.pseudos.empty(e) + }, + header: function(e) { + return q.test(e.nodeName) + }, + input: function(e) { + return N.test(e.nodeName) + }, + button: function(e) { + return fe(e, "input") && "button" === e.type || fe(e, "button") + }, + text: function(e) { + var t; + return fe(e, "input") && "text" === e.type && (null == (t = e.getAttribute("type")) || "text" === t.toLowerCase()) + }, + first: X(function() { + return [0] + }), + last: X(function(e, t) { + return [t - 1] + }), + eq: X(function(e, t, n) { + return [n < 0 ? n + t : n] + }), + even: X(function(e, t) { + for (var n = 0; n < t; n += 2) e.push(n); + return e + }), + odd: X(function(e, t) { + for (var n = 1; n < t; n += 2) e.push(n); + return e + }), + lt: X(function(e, t, n) { + var r; + for (r = n < 0 ? n + t : t < n ? t : n; 0 <= --r;) e.push(r); + return e + }), + gt: X(function(e, t, n) { + for (var r = n < 0 ? n + t : n; ++r < t;) e.push(r); + return e + }) + } + }).pseudos.nth = b.pseudos.eq, { + radio: !0, + checkbox: !0, + file: !0, + password: !0, + image: !0 + }) b.pseudos[e] = B(e); + for (e in { + submit: !0, + reset: !0 + }) b.pseudos[e] = _(e); + + function G() {} + + function Y(e, t) { + var n, r, i, o, a, s, u, l = c[e + " "]; + if (l) return t ? 0 : l.slice(0); + a = e, s = [], u = b.preFilter; + while (a) { + for (o in n && !(r = y.exec(a)) || (r && (a = a.slice(r[0].length) || a), s.push(i = [])), n = !1, (r = m.exec(a)) && (n = r.shift(), i.push({ + value: n, + type: r[0].replace(ve, " ") + }), a = a.slice(n.length)), b.filter) !(r = D[o].exec(a)) || u[o] && !(r = u[o](r)) || (n = r.shift(), i.push({ + value: n, + type: o, + matches: r + }), a = a.slice(n.length)); + if (!n) break + } + return t ? a.length : a ? I.error(e) : c(e, s).slice(0) + } + + function Q(e) { + for (var t = 0, n = e.length, r = ""; t < n; t++) r += e[t].value; + return r + } + + function J(a, e, t) { + var s = e.dir, + u = e.next, + l = u || s, + c = t && "parentNode" === l, + f = n++; + return e.first ? function(e, t, n) { + while (e = e[s]) + if (1 === e.nodeType || c) return a(e, t, n); + return !1 + } : function(e, t, n) { + var r, i, o = [E, f]; + if (n) { + while (e = e[s]) + if ((1 === e.nodeType || c) && a(e, t, n)) return !0 + } else + while (e = e[s]) + if (1 === e.nodeType || c) + if (i = e[S] || (e[S] = {}), u && fe(e, u)) e = e[s] || e; + else { + if ((r = i[l]) && r[0] === E && r[1] === f) return o[2] = r[2]; + if ((i[l] = o)[2] = a(e, t, n)) return !0 + } return !1 + } + } + + function K(i) { + return 1 < i.length ? function(e, t, n) { + var r = i.length; + while (r--) + if (!i[r](e, t, n)) return !1; + return !0 + } : i[0] + } + + function Z(e, t, n, r, i) { + for (var o, a = [], s = 0, u = e.length, l = null != t; s < u; s++)(o = e[s]) && (n && !n(o, r, i) || (a.push(o), l && t.push(s))); + return a + } + + function ee(d, h, g, v, y, e) { + return v && !v[S] && (v = ee(v)), y && !y[S] && (y = ee(y, e)), F(function(e, t, n, r) { + var i, o, a, s, u = [], + l = [], + c = t.length, + f = e || function(e, t, n) { + for (var r = 0, i = t.length; r < i; r++) I(e, t[r], n); + return n + }(h || "*", n.nodeType ? [n] : n, []), + p = !d || !e && h ? f : Z(f, u, d, n, r); + if (g ? g(p, s = y || (e ? d : c || v) ? [] : t, n, r) : s = p, v) { + i = Z(s, l), v(i, [], n, r), o = i.length; + while (o--)(a = i[o]) && (s[l[o]] = !(p[l[o]] = a)) + } + if (e) { + if (y || d) { + if (y) { + i = [], o = s.length; + while (o--)(a = s[o]) && i.push(p[o] = a); + y(null, s = [], i, r) + } + o = s.length; + while (o--)(a = s[o]) && -1 < (i = y ? se.call(e, a) : u[o]) && (e[i] = !(t[i] = a)) + } + } else s = Z(s === t ? s.splice(c, s.length) : s), y ? y(null, t, s, r) : k.apply(t, s) + }) + } + + function te(e) { + for (var i, t, n, r = e.length, o = b.relative[e[0].type], a = o || b.relative[" "], s = o ? 1 : 0, u = J(function(e) { + return e === i + }, a, !0), l = J(function(e) { + return -1 < se.call(i, e) + }, a, !0), c = [function(e, t, n) { + var r = !o && (n || t != w) || ((i = t).nodeType ? u(e, t, n) : l(e, t, n)); + return i = null, r + }]; s < r; s++) + if (t = b.relative[e[s].type]) c = [J(K(c), t)]; + else { + if ((t = b.filter[e[s].type].apply(null, e[s].matches))[S]) { + for (n = ++s; n < r; n++) + if (b.relative[e[n].type]) break; + return ee(1 < s && K(c), 1 < s && Q(e.slice(0, s - 1).concat({ + value: " " === e[s - 2].type ? "*" : "" + })).replace(ve, "$1"), t, s < n && te(e.slice(s, n)), n < r && te(e = e.slice(n)), n < r && Q(e)) + } + c.push(t) + } return K(c) + } + + function ne(e, t) { + var n, v, y, m, x, r, i = [], + o = [], + a = u[e + " "]; + if (!a) { + t || (t = Y(e)), n = t.length; + while (n--)(a = te(t[n]))[S] ? i.push(a) : o.push(a); + (a = u(e, (v = o, m = 0 < (y = i).length, x = 0 < v.length, r = function(e, t, n, r, i) { + var o, a, s, u = 0, + l = "0", + c = e && [], + f = [], + p = w, + d = e || x && b.find.TAG("*", i), + h = E += null == p ? 1 : Math.random() || .1, + g = d.length; + for (i && (w = t == T || t || i); l !== g && null != (o = d[l]); l++) { + if (x && o) { + a = 0, t || o.ownerDocument == T || (V(o), n = !C); + while (s = v[a++]) + if (s(o, t || T, n)) { + k.call(r, o); + break + } i && (E = h) + } + m && ((o = !s && o) && u--, e && c.push(o)) + } + if (u += l, m && l !== u) { + a = 0; + while (s = y[a++]) s(c, f, t, n); + if (e) { + if (0 < u) + while (l--) c[l] || f[l] || (f[l] = pe.call(r)); + f = Z(f) + } + k.apply(r, f), i && !e && 0 < f.length && 1 < u + y.length && ce.uniqueSort(r) + } + return i && (E = h, w = p), c + }, m ? F(r) : r))).selector = e + } + return a + } + + function re(e, t, n, r) { + var i, o, a, s, u, l = "function" == typeof e && e, + c = !r && Y(e = l.selector || e); + if (n = n || [], 1 === c.length) { + if (2 < (o = c[0] = c[0].slice(0)).length && "ID" === (a = o[0]).type && 9 === t.nodeType && C && b.relative[o[1].type]) { + if (!(t = (b.find.ID(a.matches[0].replace(O, P), t) || [])[0])) return n; + l && (t = t.parentNode), e = e.slice(o.shift().value.length) + } + i = D.needsContext.test(e) ? 0 : o.length; + while (i--) { + if (a = o[i], b.relative[s = a.type]) break; + if ((u = b.find[s]) && (r = u(a.matches[0].replace(O, P), H.test(o[0].type) && U(t.parentNode) || t))) { + if (o.splice(i, 1), !(e = r.length && Q(o))) return k.apply(n, r), n; + break + } + } + } + return (l || ne(e, c))(r, t, !C, n, !t || H.test(e) && U(t.parentNode) || t), n + } + G.prototype = b.filters = b.pseudos, b.setFilters = new G, le.sortStable = S.split("").sort(l).join("") === S, V(), le.sortDetached = $(function(e) { + return 1 & e.compareDocumentPosition(T.createElement("fieldset")) + }), ce.find = I, ce.expr[":"] = ce.expr.pseudos, ce.unique = ce.uniqueSort, I.compile = ne, I.select = re, I.setDocument = V, I.tokenize = Y, I.escape = ce.escapeSelector, I.getText = ce.text, I.isXML = ce.isXMLDoc, I.selectors = ce.expr, I.support = ce.support, I.uniqueSort = ce.uniqueSort + }(); + var d = function(e, t, n) { + var r = [], + i = void 0 !== n; + while ((e = e[t]) && 9 !== e.nodeType) + if (1 === e.nodeType) { + if (i && ce(e).is(n)) break; + r.push(e) + } return r + }, + h = function(e, t) { + for (var n = []; e; e = e.nextSibling) 1 === e.nodeType && e !== t && n.push(e); + return n + }, + b = ce.expr.match.needsContext, + w = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i; + + function T(e, n, r) { + return v(n) ? ce.grep(e, function(e, t) { + return !!n.call(e, t, e) !== r + }) : n.nodeType ? ce.grep(e, function(e) { + return e === n !== r + }) : "string" != typeof n ? ce.grep(e, function(e) { + return -1 < se.call(n, e) !== r + }) : ce.filter(n, e, r) + } + ce.filter = function(e, t, n) { + var r = t[0]; + return n && (e = ":not(" + e + ")"), 1 === t.length && 1 === r.nodeType ? ce.find.matchesSelector(r, e) ? [r] : [] : ce.find.matches(e, ce.grep(t, function(e) { + return 1 === e.nodeType + })) + }, ce.fn.extend({ + find: function(e) { + var t, n, r = this.length, + i = this; + if ("string" != typeof e) return this.pushStack(ce(e).filter(function() { + for (t = 0; t < r; t++) + if (ce.contains(i[t], this)) return !0 + })); + for (n = this.pushStack([]), t = 0; t < r; t++) ce.find(e, i[t], n); + return 1 < r ? ce.uniqueSort(n) : n + }, + filter: function(e) { + return this.pushStack(T(this, e || [], !1)) + }, + not: function(e) { + return this.pushStack(T(this, e || [], !0)) + }, + is: function(e) { + return !!T(this, "string" == typeof e && b.test(e) ? ce(e) : e || [], !1).length + } + }); + var k, S = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/; + (ce.fn.init = function(e, t, n) { + var r, i; + if (!e) return this; + if (n = n || k, "string" == typeof e) { + if (!(r = "<" === e[0] && ">" === e[e.length - 1] && 3 <= e.length ? [null, e, null] : S.exec(e)) || !r[1] && t) return !t || t.jquery ? (t || n).find(e) : this.constructor(t).find(e); + if (r[1]) { + if (t = t instanceof ce ? t[0] : t, ce.merge(this, ce.parseHTML(r[1], t && t.nodeType ? t.ownerDocument || t : C, !0)), w.test(r[1]) && ce.isPlainObject(t)) + for (r in t) v(this[r]) ? this[r](t[r]) : this.attr(r, t[r]); + return this + } + return (i = C.getElementById(r[2])) && (this[0] = i, this.length = 1), this + } + return e.nodeType ? (this[0] = e, this.length = 1, this) : v(e) ? void 0 !== n.ready ? n.ready(e) : e(ce) : ce.makeArray(e, this) + }).prototype = ce.fn, k = ce(C); + var E = /^(?:parents|prev(?:Until|All))/, + j = { + children: !0, + contents: !0, + next: !0, + prev: !0 + }; + + function A(e, t) { + while ((e = e[t]) && 1 !== e.nodeType); + return e + } + ce.fn.extend({ + has: function(e) { + var t = ce(e, this), + n = t.length; + return this.filter(function() { + for (var e = 0; e < n; e++) + if (ce.contains(this, t[e])) return !0 + }) + }, + closest: function(e, t) { + var n, r = 0, + i = this.length, + o = [], + a = "string" != typeof e && ce(e); + if (!b.test(e)) + for (; r < i; r++) + for (n = this[r]; n && n !== t; n = n.parentNode) + if (n.nodeType < 11 && (a ? -1 < a.index(n) : 1 === n.nodeType && ce.find.matchesSelector(n, e))) { + o.push(n); + break + } return this.pushStack(1 < o.length ? ce.uniqueSort(o) : o) + }, + index: function(e) { + return e ? "string" == typeof e ? se.call(ce(e), this[0]) : se.call(this, e.jquery ? e[0] : e) : this[0] && this[0].parentNode ? this.first().prevAll().length : -1 + }, + add: function(e, t) { + return this.pushStack(ce.uniqueSort(ce.merge(this.get(), ce(e, t)))) + }, + addBack: function(e) { + return this.add(null == e ? this.prevObject : this.prevObject.filter(e)) + } + }), ce.each({ + parent: function(e) { + var t = e.parentNode; + return t && 11 !== t.nodeType ? t : null + }, + parents: function(e) { + return d(e, "parentNode") + }, + parentsUntil: function(e, t, n) { + return d(e, "parentNode", n) + }, + next: function(e) { + return A(e, "nextSibling") + }, + prev: function(e) { + return A(e, "previousSibling") + }, + nextAll: function(e) { + return d(e, "nextSibling") + }, + prevAll: function(e) { + return d(e, "previousSibling") + }, + nextUntil: function(e, t, n) { + return d(e, "nextSibling", n) + }, + prevUntil: function(e, t, n) { + return d(e, "previousSibling", n) + }, + siblings: function(e) { + return h((e.parentNode || {}).firstChild, e) + }, + children: function(e) { + return h(e.firstChild) + }, + contents: function(e) { + return null != e.contentDocument && r(e.contentDocument) ? e.contentDocument : (fe(e, "template") && (e = e.content || e), ce.merge([], e.childNodes)) + } + }, function(r, i) { + ce.fn[r] = function(e, t) { + var n = ce.map(this, i, e); + return "Until" !== r.slice(-5) && (t = e), t && "string" == typeof t && (n = ce.filter(t, n)), 1 < this.length && (j[r] || ce.uniqueSort(n), E.test(r) && n.reverse()), this.pushStack(n) + } + }); + var D = /[^\x20\t\r\n\f]+/g; + + function N(e) { + return e + } + + function q(e) { + throw e + } + + function L(e, t, n, r) { + var i; + try { + e && v(i = e.promise) ? i.call(e).done(t).fail(n) : e && v(i = e.then) ? i.call(e, t, n) : t.apply(void 0, [e].slice(r)) + } catch (e) { + n.apply(void 0, [e]) + } + } + ce.Callbacks = function(r) { + var e, n; + r = "string" == typeof r ? (e = r, n = {}, ce.each(e.match(D) || [], function(e, t) { + n[t] = !0 + }), n) : ce.extend({}, r); + var i, t, o, a, s = [], + u = [], + l = -1, + c = function() { + for (a = a || r.once, o = i = !0; u.length; l = -1) { + t = u.shift(); + while (++l < s.length) !1 === s[l].apply(t[0], t[1]) && r.stopOnFalse && (l = s.length, t = !1) + } + r.memory || (t = !1), i = !1, a && (s = t ? [] : "") + }, + f = { + add: function() { + return s && (t && !i && (l = s.length - 1, u.push(t)), function n(e) { + ce.each(e, function(e, t) { + v(t) ? r.unique && f.has(t) || s.push(t) : t && t.length && "string" !== x(t) && n(t) + }) + }(arguments), t && !i && c()), this + }, + remove: function() { + return ce.each(arguments, function(e, t) { + var n; + while (-1 < (n = ce.inArray(t, s, n))) s.splice(n, 1), n <= l && l-- + }), this + }, + has: function(e) { + return e ? -1 < ce.inArray(e, s) : 0 < s.length + }, + empty: function() { + return s && (s = []), this + }, + disable: function() { + return a = u = [], s = t = "", this + }, + disabled: function() { + return !s + }, + lock: function() { + return a = u = [], t || i || (s = t = ""), this + }, + locked: function() { + return !!a + }, + fireWith: function(e, t) { + return a || (t = [e, (t = t || []).slice ? t.slice() : t], u.push(t), i || c()), this + }, + fire: function() { + return f.fireWith(this, arguments), this + }, + fired: function() { + return !!o + } + }; + return f + }, ce.extend({ + Deferred: function(e) { + var o = [ + ["notify", "progress", ce.Callbacks("memory"), ce.Callbacks("memory"), 2], + ["resolve", "done", ce.Callbacks("once memory"), ce.Callbacks("once memory"), 0, "resolved"], + ["reject", "fail", ce.Callbacks("once memory"), ce.Callbacks("once memory"), 1, "rejected"] + ], + i = "pending", + a = { + state: function() { + return i + }, + always: function() { + return s.done(arguments).fail(arguments), this + }, + "catch": function(e) { + return a.then(null, e) + }, + pipe: function() { + var i = arguments; + return ce.Deferred(function(r) { + ce.each(o, function(e, t) { + var n = v(i[t[4]]) && i[t[4]]; + s[t[1]](function() { + var e = n && n.apply(this, arguments); + e && v(e.promise) ? e.promise().progress(r.notify).done(r.resolve).fail(r.reject) : r[t[0] + "With"](this, n ? [e] : arguments) + }) + }), i = null + }).promise() + }, + then: function(t, n, r) { + var u = 0; + + function l(i, o, a, s) { + return function() { + var n = this, + r = arguments, + e = function() { + var e, t; + if (!(i < u)) { + if ((e = a.apply(n, r)) === o.promise()) throw new TypeError("Thenable self-resolution"); + t = e && ("object" == typeof e || "function" == typeof e) && e.then, v(t) ? s ? t.call(e, l(u, o, N, s), l(u, o, q, s)) : (u++, t.call(e, l(u, o, N, s), l(u, o, q, s), l(u, o, N, o.notifyWith))) : (a !== N && (n = void 0, r = [e]), (s || o.resolveWith)(n, r)) + } + }, + t = s ? e : function() { + try { + e() + } catch (e) { + ce.Deferred.exceptionHook && ce.Deferred.exceptionHook(e, t.error), u <= i + 1 && (a !== q && (n = void 0, r = [e]), o.rejectWith(n, r)) + } + }; + i ? t() : (ce.Deferred.getErrorHook ? t.error = ce.Deferred.getErrorHook() : ce.Deferred.getStackHook && (t.error = ce.Deferred.getStackHook()), ie.setTimeout(t)) + } + } + return ce.Deferred(function(e) { + o[0][3].add(l(0, e, v(r) ? r : N, e.notifyWith)), o[1][3].add(l(0, e, v(t) ? t : N)), o[2][3].add(l(0, e, v(n) ? n : q)) + }).promise() + }, + promise: function(e) { + return null != e ? ce.extend(e, a) : a + } + }, + s = {}; + return ce.each(o, function(e, t) { + var n = t[2], + r = t[5]; + a[t[1]] = n.add, r && n.add(function() { + i = r + }, o[3 - e][2].disable, o[3 - e][3].disable, o[0][2].lock, o[0][3].lock), n.add(t[3].fire), s[t[0]] = function() { + return s[t[0] + "With"](this === s ? void 0 : this, arguments), this + }, s[t[0] + "With"] = n.fireWith + }), a.promise(s), e && e.call(s, s), s + }, + when: function(e) { + var n = arguments.length, + t = n, + r = Array(t), + i = ae.call(arguments), + o = ce.Deferred(), + a = function(t) { + return function(e) { + r[t] = this, i[t] = 1 < arguments.length ? ae.call(arguments) : e, --n || o.resolveWith(r, i) + } + }; + if (n <= 1 && (L(e, o.done(a(t)).resolve, o.reject, !n), "pending" === o.state() || v(i[t] && i[t].then))) return o.then(); + while (t--) L(i[t], a(t), o.reject); + return o.promise() + } + }); + var H = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + ce.Deferred.exceptionHook = function(e, t) { + ie.console && ie.console.warn && e && H.test(e.name) && ie.console.warn("jQuery.Deferred exception: " + e.message, e.stack, t) + }, ce.readyException = function(e) { + ie.setTimeout(function() { + throw e + }) + }; + var O = ce.Deferred(); + + function P() { + C.removeEventListener("DOMContentLoaded", P), ie.removeEventListener("load", P), ce.ready() + } + ce.fn.ready = function(e) { + return O.then(e)["catch"](function(e) { + ce.readyException(e) + }), this + }, ce.extend({ + isReady: !1, + readyWait: 1, + ready: function(e) { + (!0 === e ? --ce.readyWait : ce.isReady) || (ce.isReady = !0) !== e && 0 < --ce.readyWait || O.resolveWith(C, [ce]) + } + }), ce.ready.then = O.then, "complete" === C.readyState || "loading" !== C.readyState && !C.documentElement.doScroll ? ie.setTimeout(ce.ready) : (C.addEventListener("DOMContentLoaded", P), ie.addEventListener("load", P)); + var M = function(e, t, n, r, i, o, a) { + var s = 0, + u = e.length, + l = null == n; + if ("object" === x(n)) + for (s in i = !0, n) M(e, t, s, n[s], !0, o, a); + else if (void 0 !== r && (i = !0, v(r) || (a = !0), l && (a ? (t.call(e, r), t = null) : (l = t, t = function(e, t, n) { + return l.call(ce(e), n) + })), t)) + for (; s < u; s++) t(e[s], n, a ? r : r.call(e[s], s, t(e[s], n))); + return i ? e : l ? t.call(e) : u ? t(e[0], n) : o + }, + R = /^-ms-/, + I = /-([a-z])/g; + + function W(e, t) { + return t.toUpperCase() + } + + function F(e) { + return e.replace(R, "ms-").replace(I, W) + } + var $ = function(e) { + return 1 === e.nodeType || 9 === e.nodeType || !+e.nodeType + }; + + function B() { + this.expando = ce.expando + B.uid++ + } + B.uid = 1, B.prototype = { + cache: function(e) { + var t = e[this.expando]; + return t || (t = {}, $(e) && (e.nodeType ? e[this.expando] = t : Object.defineProperty(e, this.expando, { + value: t, + configurable: !0 + }))), t + }, + set: function(e, t, n) { + var r, i = this.cache(e); + if ("string" == typeof t) i[F(t)] = n; + else + for (r in t) i[F(r)] = t[r]; + return i + }, + get: function(e, t) { + return void 0 === t ? this.cache(e) : e[this.expando] && e[this.expando][F(t)] + }, + access: function(e, t, n) { + return void 0 === t || t && "string" == typeof t && void 0 === n ? this.get(e, t) : (this.set(e, t, n), void 0 !== n ? n : t) + }, + remove: function(e, t) { + var n, r = e[this.expando]; + if (void 0 !== r) { + if (void 0 !== t) { + n = (t = Array.isArray(t) ? t.map(F) : (t = F(t)) in r ? [t] : t.match(D) || []).length; + while (n--) delete r[t[n]] + }(void 0 === t || ce.isEmptyObject(r)) && (e.nodeType ? e[this.expando] = void 0 : delete e[this.expando]) + } + }, + hasData: function(e) { + var t = e[this.expando]; + return void 0 !== t && !ce.isEmptyObject(t) + } + }; + var _ = new B, + z = new B, + X = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + U = /[A-Z]/g; + + function V(e, t, n) { + var r, i; + if (void 0 === n && 1 === e.nodeType) + if (r = "data-" + t.replace(U, "-$&").toLowerCase(), "string" == typeof(n = e.getAttribute(r))) { + try { + n = "true" === (i = n) || "false" !== i && ("null" === i ? null : i === +i + "" ? +i : X.test(i) ? JSON.parse(i) : i) + } catch (e) {} + z.set(e, t, n) + } else n = void 0; + return n + } + ce.extend({ + hasData: function(e) { + return z.hasData(e) || _.hasData(e) + }, + data: function(e, t, n) { + return z.access(e, t, n) + }, + removeData: function(e, t) { + z.remove(e, t) + }, + _data: function(e, t, n) { + return _.access(e, t, n) + }, + _removeData: function(e, t) { + _.remove(e, t) + } + }), ce.fn.extend({ + data: function(n, e) { + var t, r, i, o = this[0], + a = o && o.attributes; + if (void 0 === n) { + if (this.length && (i = z.get(o), 1 === o.nodeType && !_.get(o, "hasDataAttrs"))) { + t = a.length; + while (t--) a[t] && 0 === (r = a[t].name).indexOf("data-") && (r = F(r.slice(5)), V(o, r, i[r])); + _.set(o, "hasDataAttrs", !0) + } + return i + } + return "object" == typeof n ? this.each(function() { + z.set(this, n) + }) : M(this, function(e) { + var t; + if (o && void 0 === e) return void 0 !== (t = z.get(o, n)) ? t : void 0 !== (t = V(o, n)) ? t : void 0; + this.each(function() { + z.set(this, n, e) + }) + }, null, e, 1 < arguments.length, null, !0) + }, + removeData: function(e) { + return this.each(function() { + z.remove(this, e) + }) + } + }), ce.extend({ + queue: function(e, t, n) { + var r; + if (e) return t = (t || "fx") + "queue", r = _.get(e, t), n && (!r || Array.isArray(n) ? r = _.access(e, t, ce.makeArray(n)) : r.push(n)), r || [] + }, + dequeue: function(e, t) { + t = t || "fx"; + var n = ce.queue(e, t), + r = n.length, + i = n.shift(), + o = ce._queueHooks(e, t); + "inprogress" === i && (i = n.shift(), r--), i && ("fx" === t && n.unshift("inprogress"), delete o.stop, i.call(e, function() { + ce.dequeue(e, t) + }, o)), !r && o && o.empty.fire() + }, + _queueHooks: function(e, t) { + var n = t + "queueHooks"; + return _.get(e, n) || _.access(e, n, { + empty: ce.Callbacks("once memory").add(function() { + _.remove(e, [t + "queue", n]) + }) + }) + } + }), ce.fn.extend({ + queue: function(t, n) { + var e = 2; + return "string" != typeof t && (n = t, t = "fx", e--), arguments.length < e ? ce.queue(this[0], t) : void 0 === n ? this : this.each(function() { + var e = ce.queue(this, t, n); + ce._queueHooks(this, t), "fx" === t && "inprogress" !== e[0] && ce.dequeue(this, t) + }) + }, + dequeue: function(e) { + return this.each(function() { + ce.dequeue(this, e) + }) + }, + clearQueue: function(e) { + return this.queue(e || "fx", []) + }, + promise: function(e, t) { + var n, r = 1, + i = ce.Deferred(), + o = this, + a = this.length, + s = function() { + --r || i.resolveWith(o, [o]) + }; + "string" != typeof e && (t = e, e = void 0), e = e || "fx"; + while (a--)(n = _.get(o[a], e + "queueHooks")) && n.empty && (r++, n.empty.add(s)); + return s(), i.promise(t) + } + }); + var G = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + Y = new RegExp("^(?:([+-])=|)(" + G + ")([a-z%]*)$", "i"), + Q = ["Top", "Right", "Bottom", "Left"], + J = C.documentElement, + K = function(e) { + return ce.contains(e.ownerDocument, e) + }, + Z = { + composed: !0 + }; + J.getRootNode && (K = function(e) { + return ce.contains(e.ownerDocument, e) || e.getRootNode(Z) === e.ownerDocument + }); + var ee = function(e, t) { + return "none" === (e = t || e).style.display || "" === e.style.display && K(e) && "none" === ce.css(e, "display") + }; + + function te(e, t, n, r) { + var i, o, a = 20, + s = r ? function() { + return r.cur() + } : function() { + return ce.css(e, t, "") + }, + u = s(), + l = n && n[3] || (ce.cssNumber[t] ? "" : "px"), + c = e.nodeType && (ce.cssNumber[t] || "px" !== l && +u) && Y.exec(ce.css(e, t)); + if (c && c[3] !== l) { + u /= 2, l = l || c[3], c = +u || 1; + while (a--) ce.style(e, t, c + l), (1 - o) * (1 - (o = s() / u || .5)) <= 0 && (a = 0), c /= o; + c *= 2, ce.style(e, t, c + l), n = n || [] + } + return n && (c = +c || +u || 0, i = n[1] ? c + (n[1] + 1) * n[2] : +n[2], r && (r.unit = l, r.start = c, r.end = i)), i + } + var ne = {}; + + function re(e, t) { + for (var n, r, i, o, a, s, u, l = [], c = 0, f = e.length; c < f; c++)(r = e[c]).style && (n = r.style.display, t ? ("none" === n && (l[c] = _.get(r, "display") || null, l[c] || (r.style.display = "")), "" === r.style.display && ee(r) && (l[c] = (u = a = o = void 0, a = (i = r).ownerDocument, s = i.nodeName, (u = ne[s]) || (o = a.body.appendChild(a.createElement(s)), u = ce.css(o, "display"), o.parentNode.removeChild(o), "none" === u && (u = "block"), ne[s] = u)))) : "none" !== n && (l[c] = "none", _.set(r, "display", n))); + for (c = 0; c < f; c++) null != l[c] && (e[c].style.display = l[c]); + return e + } + ce.fn.extend({ + show: function() { + return re(this, !0) + }, + hide: function() { + return re(this) + }, + toggle: function(e) { + return "boolean" == typeof e ? e ? this.show() : this.hide() : this.each(function() { + ee(this) ? ce(this).show() : ce(this).hide() + }) + } + }); + var xe, be, we = /^(?:checkbox|radio)$/i, + Te = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i, + Ce = /^$|^module$|\/(?:java|ecma)script/i; + xe = C.createDocumentFragment().appendChild(C.createElement("div")), (be = C.createElement("input")).setAttribute("type", "radio"), be.setAttribute("checked", "checked"), be.setAttribute("name", "t"), xe.appendChild(be), le.checkClone = xe.cloneNode(!0).cloneNode(!0).lastChild.checked, xe.innerHTML = "", le.noCloneChecked = !!xe.cloneNode(!0).lastChild.defaultValue, xe.innerHTML = "", le.option = !!xe.lastChild; + var ke = { + thead: [1, "", "
"], + col: [2, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + _default: [0, "", ""] + }; + + function Se(e, t) { + var n; + return n = "undefined" != typeof e.getElementsByTagName ? e.getElementsByTagName(t || "*") : "undefined" != typeof e.querySelectorAll ? e.querySelectorAll(t || "*") : [], void 0 === t || t && fe(e, t) ? ce.merge([e], n) : n + } + + function Ee(e, t) { + for (var n = 0, r = e.length; n < r; n++) _.set(e[n], "globalEval", !t || _.get(t[n], "globalEval")) + } + ke.tbody = ke.tfoot = ke.colgroup = ke.caption = ke.thead, ke.th = ke.td, le.option || (ke.optgroup = ke.option = [1, ""]); + var je = /<|&#?\w+;/; + + function Ae(e, t, n, r, i) { + for (var o, a, s, u, l, c, f = t.createDocumentFragment(), p = [], d = 0, h = e.length; d < h; d++) + if ((o = e[d]) || 0 === o) + if ("object" === x(o)) ce.merge(p, o.nodeType ? [o] : o); + else if (je.test(o)) { + a = a || f.appendChild(t.createElement("div")), s = (Te.exec(o) || ["", ""])[1].toLowerCase(), u = ke[s] || ke._default, a.innerHTML = u[1] + ce.htmlPrefilter(o) + u[2], c = u[0]; + while (c--) a = a.lastChild; + ce.merge(p, a.childNodes), (a = f.firstChild).textContent = "" + } else p.push(t.createTextNode(o)); + f.textContent = "", d = 0; + while (o = p[d++]) + if (r && -1 < ce.inArray(o, r)) i && i.push(o); + else if (l = K(o), a = Se(f.appendChild(o), "script"), l && Ee(a), n) { + c = 0; + while (o = a[c++]) Ce.test(o.type || "") && n.push(o) + } + return f + } + var De = /^([^.]*)(?:\.(.+)|)/; + + function Ne() { + return !0 + } + + function qe() { + return !1 + } + + function Le(e, t, n, r, i, o) { + var a, s; + if ("object" == typeof t) { + for (s in "string" != typeof n && (r = r || n, n = void 0), t) Le(e, s, n, r, t[s], o); + return e + } + if (null == r && null == i ? (i = n, r = n = void 0) : null == i && ("string" == typeof n ? (i = r, r = void 0) : (i = r, r = n, n = void 0)), !1 === i) i = qe; + else if (!i) return e; + return 1 === o && (a = i, (i = function(e) { + return ce().off(e), a.apply(this, arguments) + }).guid = a.guid || (a.guid = ce.guid++)), e.each(function() { + ce.event.add(this, t, i, r, n) + }) + } + + function He(e, r, t) { + t ? (_.set(e, r, !1), ce.event.add(e, r, { + namespace: !1, + handler: function(e) { + var t, n = _.get(this, r); + if (1 & e.isTrigger && this[r]) { + if (n)(ce.event.special[r] || {}).delegateType && e.stopPropagation(); + else if (n = ae.call(arguments), _.set(this, r, n), this[r](), t = _.get(this, r), _.set(this, r, !1), n !== t) return e.stopImmediatePropagation(), e.preventDefault(), t + } else n && (_.set(this, r, ce.event.trigger(n[0], n.slice(1), this)), e.stopPropagation(), e.isImmediatePropagationStopped = Ne) + } + })) : void 0 === _.get(e, r) && ce.event.add(e, r, Ne) + } + ce.event = { + global: {}, + add: function(t, e, n, r, i) { + var o, a, s, u, l, c, f, p, d, h, g, v = _.get(t); + if ($(t)) { + n.handler && (n = (o = n).handler, i = o.selector), i && ce.find.matchesSelector(J, i), n.guid || (n.guid = ce.guid++), (u = v.events) || (u = v.events = Object.create(null)), (a = v.handle) || (a = v.handle = function(e) { + return "undefined" != typeof ce && ce.event.triggered !== e.type ? ce.event.dispatch.apply(t, arguments) : void 0 + }), l = (e = (e || "").match(D) || [""]).length; + while (l--) d = g = (s = De.exec(e[l]) || [])[1], h = (s[2] || "").split(".").sort(), d && (f = ce.event.special[d] || {}, d = (i ? f.delegateType : f.bindType) || d, f = ce.event.special[d] || {}, c = ce.extend({ + type: d, + origType: g, + data: r, + handler: n, + guid: n.guid, + selector: i, + needsContext: i && ce.expr.match.needsContext.test(i), + namespace: h.join(".") + }, o), (p = u[d]) || ((p = u[d] = []).delegateCount = 0, f.setup && !1 !== f.setup.call(t, r, h, a) || t.addEventListener && t.addEventListener(d, a)), f.add && (f.add.call(t, c), c.handler.guid || (c.handler.guid = n.guid)), i ? p.splice(p.delegateCount++, 0, c) : p.push(c), ce.event.global[d] = !0) + } + }, + remove: function(e, t, n, r, i) { + var o, a, s, u, l, c, f, p, d, h, g, v = _.hasData(e) && _.get(e); + if (v && (u = v.events)) { + l = (t = (t || "").match(D) || [""]).length; + while (l--) + if (d = g = (s = De.exec(t[l]) || [])[1], h = (s[2] || "").split(".").sort(), d) { + f = ce.event.special[d] || {}, p = u[d = (r ? f.delegateType : f.bindType) || d] || [], s = s[2] && new RegExp("(^|\\.)" + h.join("\\.(?:.*\\.|)") + "(\\.|$)"), a = o = p.length; + while (o--) c = p[o], !i && g !== c.origType || n && n.guid !== c.guid || s && !s.test(c.namespace) || r && r !== c.selector && ("**" !== r || !c.selector) || (p.splice(o, 1), c.selector && p.delegateCount--, f.remove && f.remove.call(e, c)); + a && !p.length && (f.teardown && !1 !== f.teardown.call(e, h, v.handle) || ce.removeEvent(e, d, v.handle), delete u[d]) + } else + for (d in u) ce.event.remove(e, d + t[l], n, r, !0); + ce.isEmptyObject(u) && _.remove(e, "handle events") + } + }, + dispatch: function(e) { + var t, n, r, i, o, a, s = new Array(arguments.length), + u = ce.event.fix(e), + l = (_.get(this, "events") || Object.create(null))[u.type] || [], + c = ce.event.special[u.type] || {}; + for (s[0] = u, t = 1; t < arguments.length; t++) s[t] = arguments[t]; + if (u.delegateTarget = this, !c.preDispatch || !1 !== c.preDispatch.call(this, u)) { + a = ce.event.handlers.call(this, u, l), t = 0; + while ((i = a[t++]) && !u.isPropagationStopped()) { + u.currentTarget = i.elem, n = 0; + while ((o = i.handlers[n++]) && !u.isImmediatePropagationStopped()) u.rnamespace && !1 !== o.namespace && !u.rnamespace.test(o.namespace) || (u.handleObj = o, u.data = o.data, void 0 !== (r = ((ce.event.special[o.origType] || {}).handle || o.handler).apply(i.elem, s)) && !1 === (u.result = r) && (u.preventDefault(), u.stopPropagation())) + } + return c.postDispatch && c.postDispatch.call(this, u), u.result + } + }, + handlers: function(e, t) { + var n, r, i, o, a, s = [], + u = t.delegateCount, + l = e.target; + if (u && l.nodeType && !("click" === e.type && 1 <= e.button)) + for (; l !== this; l = l.parentNode || this) + if (1 === l.nodeType && ("click" !== e.type || !0 !== l.disabled)) { + for (o = [], a = {}, n = 0; n < u; n++) void 0 === a[i = (r = t[n]).selector + " "] && (a[i] = r.needsContext ? -1 < ce(i, this).index(l) : ce.find(i, this, null, [l]).length), a[i] && o.push(r); + o.length && s.push({ + elem: l, + handlers: o + }) + } return l = this, u < t.length && s.push({ + elem: l, + handlers: t.slice(u) + }), s + }, + addProp: function(t, e) { + Object.defineProperty(ce.Event.prototype, t, { + enumerable: !0, + configurable: !0, + get: v(e) ? function() { + if (this.originalEvent) return e(this.originalEvent) + } : function() { + if (this.originalEvent) return this.originalEvent[t] + }, + set: function(e) { + Object.defineProperty(this, t, { + enumerable: !0, + configurable: !0, + writable: !0, + value: e + }) + } + }) + }, + fix: function(e) { + return e[ce.expando] ? e : new ce.Event(e) + }, + special: { + load: { + noBubble: !0 + }, + click: { + setup: function(e) { + var t = this || e; + return we.test(t.type) && t.click && fe(t, "input") && He(t, "click", !0), !1 + }, + trigger: function(e) { + var t = this || e; + return we.test(t.type) && t.click && fe(t, "input") && He(t, "click"), !0 + }, + _default: function(e) { + var t = e.target; + return we.test(t.type) && t.click && fe(t, "input") && _.get(t, "click") || fe(t, "a") + } + }, + beforeunload: { + postDispatch: function(e) { + void 0 !== e.result && e.originalEvent && (e.originalEvent.returnValue = e.result) + } + } + } + }, ce.removeEvent = function(e, t, n) { + e.removeEventListener && e.removeEventListener(t, n) + }, ce.Event = function(e, t) { + if (!(this instanceof ce.Event)) return new ce.Event(e, t); + e && e.type ? (this.originalEvent = e, this.type = e.type, this.isDefaultPrevented = e.defaultPrevented || void 0 === e.defaultPrevented && !1 === e.returnValue ? Ne : qe, this.target = e.target && 3 === e.target.nodeType ? e.target.parentNode : e.target, this.currentTarget = e.currentTarget, this.relatedTarget = e.relatedTarget) : this.type = e, t && ce.extend(this, t), this.timeStamp = e && e.timeStamp || Date.now(), this[ce.expando] = !0 + }, ce.Event.prototype = { + constructor: ce.Event, + isDefaultPrevented: qe, + isPropagationStopped: qe, + isImmediatePropagationStopped: qe, + isSimulated: !1, + preventDefault: function() { + var e = this.originalEvent; + this.isDefaultPrevented = Ne, e && !this.isSimulated && e.preventDefault() + }, + stopPropagation: function() { + var e = this.originalEvent; + this.isPropagationStopped = Ne, e && !this.isSimulated && e.stopPropagation() + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + this.isImmediatePropagationStopped = Ne, e && !this.isSimulated && e.stopImmediatePropagation(), this.stopPropagation() + } + }, ce.each({ + altKey: !0, + bubbles: !0, + cancelable: !0, + changedTouches: !0, + ctrlKey: !0, + detail: !0, + eventPhase: !0, + metaKey: !0, + pageX: !0, + pageY: !0, + shiftKey: !0, + view: !0, + "char": !0, + code: !0, + charCode: !0, + key: !0, + keyCode: !0, + button: !0, + buttons: !0, + clientX: !0, + clientY: !0, + offsetX: !0, + offsetY: !0, + pointerId: !0, + pointerType: !0, + screenX: !0, + screenY: !0, + targetTouches: !0, + toElement: !0, + touches: !0, + which: !0 + }, ce.event.addProp), ce.each({ + focus: "focusin", + blur: "focusout" + }, function(r, i) { + function o(e) { + if (C.documentMode) { + var t = _.get(this, "handle"), + n = ce.event.fix(e); + n.type = "focusin" === e.type ? "focus" : "blur", n.isSimulated = !0, t(e), n.target === n.currentTarget && t(n) + } else ce.event.simulate(i, e.target, ce.event.fix(e)) + } + ce.event.special[r] = { + setup: function() { + var e; + if (He(this, r, !0), !C.documentMode) return !1; + (e = _.get(this, i)) || this.addEventListener(i, o), _.set(this, i, (e || 0) + 1) + }, + trigger: function() { + return He(this, r), !0 + }, + teardown: function() { + var e; + if (!C.documentMode) return !1; + (e = _.get(this, i) - 1) ? _.set(this, i, e): (this.removeEventListener(i, o), _.remove(this, i)) + }, + _default: function(e) { + return _.get(e.target, r) + }, + delegateType: i + }, ce.event.special[i] = { + setup: function() { + var e = this.ownerDocument || this.document || this, + t = C.documentMode ? this : e, + n = _.get(t, i); + n || (C.documentMode ? this.addEventListener(i, o) : e.addEventListener(r, o, !0)), _.set(t, i, (n || 0) + 1) + }, + teardown: function() { + var e = this.ownerDocument || this.document || this, + t = C.documentMode ? this : e, + n = _.get(t, i) - 1; + n ? _.set(t, i, n) : (C.documentMode ? this.removeEventListener(i, o) : e.removeEventListener(r, o, !0), _.remove(t, i)) + } + } + }), ce.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" + }, function(e, i) { + ce.event.special[e] = { + delegateType: i, + bindType: i, + handle: function(e) { + var t, n = e.relatedTarget, + r = e.handleObj; + return n && (n === this || ce.contains(this, n)) || (e.type = r.origType, t = r.handler.apply(this, arguments), e.type = i), t + } + } + }), ce.fn.extend({ + on: function(e, t, n, r) { + return Le(this, e, t, n, r) + }, + one: function(e, t, n, r) { + return Le(this, e, t, n, r, 1) + }, + off: function(e, t, n) { + var r, i; + if (e && e.preventDefault && e.handleObj) return r = e.handleObj, ce(e.delegateTarget).off(r.namespace ? r.origType + "." + r.namespace : r.origType, r.selector, r.handler), this; + if ("object" == typeof e) { + for (i in e) this.off(i, t, e[i]); + return this + } + return !1 !== t && "function" != typeof t || (n = t, t = void 0), !1 === n && (n = qe), this.each(function() { + ce.event.remove(this, e, n, t) + }) + } + }); + var Oe = /\s*$/g; + + function Re(e, t) { + return fe(e, "table") && fe(11 !== t.nodeType ? t : t.firstChild, "tr") && ce(e).children("tbody")[0] || e + } + + function Ie(e) { + return e.type = (null !== e.getAttribute("type")) + "/" + e.type, e + } + + function We(e) { + return "true/" === (e.type || "").slice(0, 5) ? e.type = e.type.slice(5) : e.removeAttribute("type"), e + } + + function Fe(e, t) { + var n, r, i, o, a, s; + if (1 === t.nodeType) { + if (_.hasData(e) && (s = _.get(e).events)) + for (i in _.remove(t, "handle events"), s) + for (n = 0, r = s[i].length; n < r; n++) ce.event.add(t, i, s[i][n]); + z.hasData(e) && (o = z.access(e), a = ce.extend({}, o), z.set(t, a)) + } + } + + function $e(n, r, i, o) { + r = g(r); + var e, t, a, s, u, l, c = 0, + f = n.length, + p = f - 1, + d = r[0], + h = v(d); + if (h || 1 < f && "string" == typeof d && !le.checkClone && Pe.test(d)) return n.each(function(e) { + var t = n.eq(e); + h && (r[0] = d.call(this, e, t.html())), $e(t, r, i, o) + }); + if (f && (t = (e = Ae(r, n[0].ownerDocument, !1, n, o)).firstChild, 1 === e.childNodes.length && (e = t), t || o)) { + for (s = (a = ce.map(Se(e, "script"), Ie)).length; c < f; c++) u = e, c !== p && (u = ce.clone(u, !0, !0), s && ce.merge(a, Se(u, "script"))), i.call(n[c], u, c); + if (s) + for (l = a[a.length - 1].ownerDocument, ce.map(a, We), c = 0; c < s; c++) u = a[c], Ce.test(u.type || "") && !_.access(u, "globalEval") && ce.contains(l, u) && (u.src && "module" !== (u.type || "").toLowerCase() ? ce._evalUrl && !u.noModule && ce._evalUrl(u.src, { + nonce: u.nonce || u.getAttribute("nonce") + }, l) : m(u.textContent.replace(Me, ""), u, l)) + } + return n + } + + function Be(e, t, n) { + for (var r, i = t ? ce.filter(t, e) : e, o = 0; null != (r = i[o]); o++) n || 1 !== r.nodeType || ce.cleanData(Se(r)), r.parentNode && (n && K(r) && Ee(Se(r, "script")), r.parentNode.removeChild(r)); + return e + } + ce.extend({ + htmlPrefilter: function(e) { + return e + }, + clone: function(e, t, n) { + var r, i, o, a, s, u, l, c = e.cloneNode(!0), + f = K(e); + if (!(le.noCloneChecked || 1 !== e.nodeType && 11 !== e.nodeType || ce.isXMLDoc(e))) + for (a = Se(c), r = 0, i = (o = Se(e)).length; r < i; r++) s = o[r], u = a[r], void 0, "input" === (l = u.nodeName.toLowerCase()) && we.test(s.type) ? u.checked = s.checked : "input" !== l && "textarea" !== l || (u.defaultValue = s.defaultValue); + if (t) + if (n) + for (o = o || Se(e), a = a || Se(c), r = 0, i = o.length; r < i; r++) Fe(o[r], a[r]); + else Fe(e, c); + return 0 < (a = Se(c, "script")).length && Ee(a, !f && Se(e, "script")), c + }, + cleanData: function(e) { + for (var t, n, r, i = ce.event.special, o = 0; void 0 !== (n = e[o]); o++) + if ($(n)) { + if (t = n[_.expando]) { + if (t.events) + for (r in t.events) i[r] ? ce.event.remove(n, r) : ce.removeEvent(n, r, t.handle); + n[_.expando] = void 0 + } + n[z.expando] && (n[z.expando] = void 0) + } + } + }), ce.fn.extend({ + detach: function(e) { + return Be(this, e, !0) + }, + remove: function(e) { + return Be(this, e) + }, + text: function(e) { + return M(this, function(e) { + return void 0 === e ? ce.text(this) : this.empty().each(function() { + 1 !== this.nodeType && 11 !== this.nodeType && 9 !== this.nodeType || (this.textContent = e) + }) + }, null, e, arguments.length) + }, + append: function() { + return $e(this, arguments, function(e) { + 1 !== this.nodeType && 11 !== this.nodeType && 9 !== this.nodeType || Re(this, e).appendChild(e) + }) + }, + prepend: function() { + return $e(this, arguments, function(e) { + if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { + var t = Re(this, e); + t.insertBefore(e, t.firstChild) + } + }) + }, + before: function() { + return $e(this, arguments, function(e) { + this.parentNode && this.parentNode.insertBefore(e, this) + }) + }, + after: function() { + return $e(this, arguments, function(e) { + this.parentNode && this.parentNode.insertBefore(e, this.nextSibling) + }) + }, + empty: function() { + for (var e, t = 0; null != (e = this[t]); t++) 1 === e.nodeType && (ce.cleanData(Se(e, !1)), e.textContent = ""); + return this + }, + clone: function(e, t) { + return e = null != e && e, t = null == t ? e : t, this.map(function() { + return ce.clone(this, e, t) + }) + }, + html: function(e) { + return M(this, function(e) { + var t = this[0] || {}, + n = 0, + r = this.length; + if (void 0 === e && 1 === t.nodeType) return t.innerHTML; + if ("string" == typeof e && !Oe.test(e) && !ke[(Te.exec(e) || ["", ""])[1].toLowerCase()]) { + e = ce.htmlPrefilter(e); + try { + for (; n < r; n++) 1 === (t = this[n] || {}).nodeType && (ce.cleanData(Se(t, !1)), t.innerHTML = e); + t = 0 + } catch (e) {} + } + t && this.empty().append(e) + }, null, e, arguments.length) + }, + replaceWith: function() { + var n = []; + return $e(this, arguments, function(e) { + var t = this.parentNode; + ce.inArray(this, n) < 0 && (ce.cleanData(Se(this)), t && t.replaceChild(e, this)) + }, n) + } + }), ce.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function(e, a) { + ce.fn[e] = function(e) { + for (var t, n = [], r = ce(e), i = r.length - 1, o = 0; o <= i; o++) t = o === i ? this : this.clone(!0), ce(r[o])[a](t), s.apply(n, t.get()); + return this.pushStack(n) + } + }); + var _e = new RegExp("^(" + G + ")(?!px)[a-z%]+$", "i"), + ze = /^--/, + Xe = function(e) { + var t = e.ownerDocument.defaultView; + return t && t.opener || (t = ie), t.getComputedStyle(e) + }, + Ue = function(e, t, n) { + var r, i, o = {}; + for (i in t) o[i] = e.style[i], e.style[i] = t[i]; + for (i in r = n.call(e), t) e.style[i] = o[i]; + return r + }, + Ve = new RegExp(Q.join("|"), "i"); + + function Ge(e, t, n) { + var r, i, o, a, s = ze.test(t), + u = e.style; + return (n = n || Xe(e)) && (a = n.getPropertyValue(t) || n[t], s && a && (a = a.replace(ve, "$1") || void 0), "" !== a || K(e) || (a = ce.style(e, t)), !le.pixelBoxStyles() && _e.test(a) && Ve.test(t) && (r = u.width, i = u.minWidth, o = u.maxWidth, u.minWidth = u.maxWidth = u.width = a, a = n.width, u.width = r, u.minWidth = i, u.maxWidth = o)), void 0 !== a ? a + "" : a + } + + function Ye(e, t) { + return { + get: function() { + if (!e()) return (this.get = t).apply(this, arguments); + delete this.get + } + } + }! function() { + function e() { + if (l) { + u.style.cssText = "position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0", l.style.cssText = "position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%", J.appendChild(u).appendChild(l); + var e = ie.getComputedStyle(l); + n = "1%" !== e.top, s = 12 === t(e.marginLeft), l.style.right = "60%", o = 36 === t(e.right), r = 36 === t(e.width), l.style.position = "absolute", i = 12 === t(l.offsetWidth / 3), J.removeChild(u), l = null + } + } + + function t(e) { + return Math.round(parseFloat(e)) + } + var n, r, i, o, a, s, u = C.createElement("div"), + l = C.createElement("div"); + l.style && (l.style.backgroundClip = "content-box", l.cloneNode(!0).style.backgroundClip = "", le.clearCloneStyle = "content-box" === l.style.backgroundClip, ce.extend(le, { + boxSizingReliable: function() { + return e(), r + }, + pixelBoxStyles: function() { + return e(), o + }, + pixelPosition: function() { + return e(), n + }, + reliableMarginLeft: function() { + return e(), s + }, + scrollboxSize: function() { + return e(), i + }, + reliableTrDimensions: function() { + var e, t, n, r; + return null == a && (e = C.createElement("table"), t = C.createElement("tr"), n = C.createElement("div"), e.style.cssText = "position:absolute;left:-11111px;border-collapse:separate", t.style.cssText = "box-sizing:content-box;border:1px solid", t.style.height = "1px", n.style.height = "9px", n.style.display = "block", J.appendChild(e).appendChild(t).appendChild(n), r = ie.getComputedStyle(t), a = parseInt(r.height, 10) + parseInt(r.borderTopWidth, 10) + parseInt(r.borderBottomWidth, 10) === t.offsetHeight, J.removeChild(e)), a + } + })) + }(); + var Qe = ["Webkit", "Moz", "ms"], + Je = C.createElement("div").style, + Ke = {}; + + function Ze(e) { + var t = ce.cssProps[e] || Ke[e]; + return t || (e in Je ? e : Ke[e] = function(e) { + var t = e[0].toUpperCase() + e.slice(1), + n = Qe.length; + while (n--) + if ((e = Qe[n] + t) in Je) return e + }(e) || e) + } + var et = /^(none|table(?!-c[ea]).+)/, + tt = { + position: "absolute", + visibility: "hidden", + display: "block" + }, + nt = { + letterSpacing: "0", + fontWeight: "400" + }; + + function rt(e, t, n) { + var r = Y.exec(t); + return r ? Math.max(0, r[2] - (n || 0)) + (r[3] || "px") : t + } + + function it(e, t, n, r, i, o) { + var a = "width" === t ? 1 : 0, + s = 0, + u = 0, + l = 0; + if (n === (r ? "border" : "content")) return 0; + for (; a < 4; a += 2) "margin" === n && (l += ce.css(e, n + Q[a], !0, i)), r ? ("content" === n && (u -= ce.css(e, "padding" + Q[a], !0, i)), "margin" !== n && (u -= ce.css(e, "border" + Q[a] + "Width", !0, i))) : (u += ce.css(e, "padding" + Q[a], !0, i), "padding" !== n ? u += ce.css(e, "border" + Q[a] + "Width", !0, i) : s += ce.css(e, "border" + Q[a] + "Width", !0, i)); + return !r && 0 <= o && (u += Math.max(0, Math.ceil(e["offset" + t[0].toUpperCase() + t.slice(1)] - o - u - s - .5)) || 0), u + l + } + + function ot(e, t, n) { + var r = Xe(e), + i = (!le.boxSizingReliable() || n) && "border-box" === ce.css(e, "boxSizing", !1, r), + o = i, + a = Ge(e, t, r), + s = "offset" + t[0].toUpperCase() + t.slice(1); + if (_e.test(a)) { + if (!n) return a; + a = "auto" + } + return (!le.boxSizingReliable() && i || !le.reliableTrDimensions() && fe(e, "tr") || "auto" === a || !parseFloat(a) && "inline" === ce.css(e, "display", !1, r)) && e.getClientRects().length && (i = "border-box" === ce.css(e, "boxSizing", !1, r), (o = s in e) && (a = e[s])), (a = parseFloat(a) || 0) + it(e, t, n || (i ? "border" : "content"), o, r, a) + "px" + } + + function at(e, t, n, r, i) { + return new at.prototype.init(e, t, n, r, i) + } + ce.extend({ + cssHooks: { + opacity: { + get: function(e, t) { + if (t) { + var n = Ge(e, "opacity"); + return "" === n ? "1" : n + } + } + } + }, + cssNumber: { + animationIterationCount: !0, + aspectRatio: !0, + borderImageSlice: !0, + columnCount: !0, + flexGrow: !0, + flexShrink: !0, + fontWeight: !0, + gridArea: !0, + gridColumn: !0, + gridColumnEnd: !0, + gridColumnStart: !0, + gridRow: !0, + gridRowEnd: !0, + gridRowStart: !0, + lineHeight: !0, + opacity: !0, + order: !0, + orphans: !0, + scale: !0, + widows: !0, + zIndex: !0, + zoom: !0, + fillOpacity: !0, + floodOpacity: !0, + stopOpacity: !0, + strokeMiterlimit: !0, + strokeOpacity: !0 + }, + cssProps: {}, + style: function(e, t, n, r) { + if (e && 3 !== e.nodeType && 8 !== e.nodeType && e.style) { + var i, o, a, s = F(t), + u = ze.test(t), + l = e.style; + if (u || (t = Ze(s)), a = ce.cssHooks[t] || ce.cssHooks[s], void 0 === n) return a && "get" in a && void 0 !== (i = a.get(e, !1, r)) ? i : l[t]; + "string" === (o = typeof n) && (i = Y.exec(n)) && i[1] && (n = te(e, t, i), o = "number"), null != n && n == n && ("number" !== o || u || (n += i && i[3] || (ce.cssNumber[s] ? "" : "px")), le.clearCloneStyle || "" !== n || 0 !== t.indexOf("background") || (l[t] = "inherit"), a && "set" in a && void 0 === (n = a.set(e, n, r)) || (u ? l.setProperty(t, n) : l[t] = n)) + } + }, + css: function(e, t, n, r) { + var i, o, a, s = F(t); + return ze.test(t) || (t = Ze(s)), (a = ce.cssHooks[t] || ce.cssHooks[s]) && "get" in a && (i = a.get(e, !0, n)), void 0 === i && (i = Ge(e, t, r)), "normal" === i && t in nt && (i = nt[t]), "" === n || n ? (o = parseFloat(i), !0 === n || isFinite(o) ? o || 0 : i) : i + } + }), ce.each(["height", "width"], function(e, u) { + ce.cssHooks[u] = { + get: function(e, t, n) { + if (t) return !et.test(ce.css(e, "display")) || e.getClientRects().length && e.getBoundingClientRect().width ? ot(e, u, n) : Ue(e, tt, function() { + return ot(e, u, n) + }) + }, + set: function(e, t, n) { + var r, i = Xe(e), + o = !le.scrollboxSize() && "absolute" === i.position, + a = (o || n) && "border-box" === ce.css(e, "boxSizing", !1, i), + s = n ? it(e, u, n, a, i) : 0; + return a && o && (s -= Math.ceil(e["offset" + u[0].toUpperCase() + u.slice(1)] - parseFloat(i[u]) - it(e, u, "border", !1, i) - .5)), s && (r = Y.exec(t)) && "px" !== (r[3] || "px") && (e.style[u] = t, t = ce.css(e, u)), rt(0, t, s) + } + } + }), ce.cssHooks.marginLeft = Ye(le.reliableMarginLeft, function(e, t) { + if (t) return (parseFloat(Ge(e, "marginLeft")) || e.getBoundingClientRect().left - Ue(e, { + marginLeft: 0 + }, function() { + return e.getBoundingClientRect().left + })) + "px" + }), ce.each({ + margin: "", + padding: "", + border: "Width" + }, function(i, o) { + ce.cssHooks[i + o] = { + expand: function(e) { + for (var t = 0, n = {}, r = "string" == typeof e ? e.split(" ") : [e]; t < 4; t++) n[i + Q[t] + o] = r[t] || r[t - 2] || r[0]; + return n + } + }, "margin" !== i && (ce.cssHooks[i + o].set = rt) + }), ce.fn.extend({ + css: function(e, t) { + return M(this, function(e, t, n) { + var r, i, o = {}, + a = 0; + if (Array.isArray(t)) { + for (r = Xe(e), i = t.length; a < i; a++) o[t[a]] = ce.css(e, t[a], !1, r); + return o + } + return void 0 !== n ? ce.style(e, t, n) : ce.css(e, t) + }, e, t, 1 < arguments.length) + } + }), ((ce.Tween = at).prototype = { + constructor: at, + init: function(e, t, n, r, i, o) { + this.elem = e, this.prop = n, this.easing = i || ce.easing._default, this.options = t, this.start = this.now = this.cur(), this.end = r, this.unit = o || (ce.cssNumber[n] ? "" : "px") + }, + cur: function() { + var e = at.propHooks[this.prop]; + return e && e.get ? e.get(this) : at.propHooks._default.get(this) + }, + run: function(e) { + var t, n = at.propHooks[this.prop]; + return this.options.duration ? this.pos = t = ce.easing[this.easing](e, this.options.duration * e, 0, 1, this.options.duration) : this.pos = t = e, this.now = (this.end - this.start) * t + this.start, this.options.step && this.options.step.call(this.elem, this.now, this), n && n.set ? n.set(this) : at.propHooks._default.set(this), this + } + }).init.prototype = at.prototype, (at.propHooks = { + _default: { + get: function(e) { + var t; + return 1 !== e.elem.nodeType || null != e.elem[e.prop] && null == e.elem.style[e.prop] ? e.elem[e.prop] : (t = ce.css(e.elem, e.prop, "")) && "auto" !== t ? t : 0 + }, + set: function(e) { + ce.fx.step[e.prop] ? ce.fx.step[e.prop](e) : 1 !== e.elem.nodeType || !ce.cssHooks[e.prop] && null == e.elem.style[Ze(e.prop)] ? e.elem[e.prop] = e.now : ce.style(e.elem, e.prop, e.now + e.unit) + } + } + }).scrollTop = at.propHooks.scrollLeft = { + set: function(e) { + e.elem.nodeType && e.elem.parentNode && (e.elem[e.prop] = e.now) + } + }, ce.easing = { + linear: function(e) { + return e + }, + swing: function(e) { + return .5 - Math.cos(e * Math.PI) / 2 + }, + _default: "swing" + }, ce.fx = at.prototype.init, ce.fx.step = {}; + var st, ut, lt, ct, ft = /^(?:toggle|show|hide)$/, + pt = /queueHooks$/; + + function dt() { + ut && (!1 === C.hidden && ie.requestAnimationFrame ? ie.requestAnimationFrame(dt) : ie.setTimeout(dt, ce.fx.interval), ce.fx.tick()) + } + + function ht() { + return ie.setTimeout(function() { + st = void 0 + }), st = Date.now() + } + + function gt(e, t) { + var n, r = 0, + i = { + height: e + }; + for (t = t ? 1 : 0; r < 4; r += 2 - t) i["margin" + (n = Q[r])] = i["padding" + n] = e; + return t && (i.opacity = i.width = e), i + } + + function vt(e, t, n) { + for (var r, i = (yt.tweeners[t] || []).concat(yt.tweeners["*"]), o = 0, a = i.length; o < a; o++) + if (r = i[o].call(n, t, e)) return r + } + + function yt(o, e, t) { + var n, a, r = 0, + i = yt.prefilters.length, + s = ce.Deferred().always(function() { + delete u.elem + }), + u = function() { + if (a) return !1; + for (var e = st || ht(), t = Math.max(0, l.startTime + l.duration - e), n = 1 - (t / l.duration || 0), r = 0, i = l.tweens.length; r < i; r++) l.tweens[r].run(n); + return s.notifyWith(o, [l, n, t]), n < 1 && i ? t : (i || s.notifyWith(o, [l, 1, 0]), s.resolveWith(o, [l]), !1) + }, + l = s.promise({ + elem: o, + props: ce.extend({}, e), + opts: ce.extend(!0, { + specialEasing: {}, + easing: ce.easing._default + }, t), + originalProperties: e, + originalOptions: t, + startTime: st || ht(), + duration: t.duration, + tweens: [], + createTween: function(e, t) { + var n = ce.Tween(o, l.opts, e, t, l.opts.specialEasing[e] || l.opts.easing); + return l.tweens.push(n), n + }, + stop: function(e) { + var t = 0, + n = e ? l.tweens.length : 0; + if (a) return this; + for (a = !0; t < n; t++) l.tweens[t].run(1); + return e ? (s.notifyWith(o, [l, 1, 0]), s.resolveWith(o, [l, e])) : s.rejectWith(o, [l, e]), this + } + }), + c = l.props; + for (! function(e, t) { + var n, r, i, o, a; + for (n in e) + if (i = t[r = F(n)], o = e[n], Array.isArray(o) && (i = o[1], o = e[n] = o[0]), n !== r && (e[r] = o, delete e[n]), (a = ce.cssHooks[r]) && "expand" in a) + for (n in o = a.expand(o), delete e[r], o) n in e || (e[n] = o[n], t[n] = i); + else t[r] = i + }(c, l.opts.specialEasing); r < i; r++) + if (n = yt.prefilters[r].call(l, o, c, l.opts)) return v(n.stop) && (ce._queueHooks(l.elem, l.opts.queue).stop = n.stop.bind(n)), n; + return ce.map(c, vt, l), v(l.opts.start) && l.opts.start.call(o, l), l.progress(l.opts.progress).done(l.opts.done, l.opts.complete).fail(l.opts.fail).always(l.opts.always), ce.fx.timer(ce.extend(u, { + elem: o, + anim: l, + queue: l.opts.queue + })), l + } + ce.Animation = ce.extend(yt, { + tweeners: { + "*": [function(e, t) { + var n = this.createTween(e, t); + return te(n.elem, e, Y.exec(t), n), n + }] + }, + tweener: function(e, t) { + v(e) ? (t = e, e = ["*"]) : e = e.match(D); + for (var n, r = 0, i = e.length; r < i; r++) n = e[r], yt.tweeners[n] = yt.tweeners[n] || [], yt.tweeners[n].unshift(t) + }, + prefilters: [function(e, t, n) { + var r, i, o, a, s, u, l, c, f = "width" in t || "height" in t, + p = this, + d = {}, + h = e.style, + g = e.nodeType && ee(e), + v = _.get(e, "fxshow"); + for (r in n.queue || (null == (a = ce._queueHooks(e, "fx")).unqueued && (a.unqueued = 0, s = a.empty.fire, a.empty.fire = function() { + a.unqueued || s() + }), a.unqueued++, p.always(function() { + p.always(function() { + a.unqueued--, ce.queue(e, "fx").length || a.empty.fire() + }) + })), t) + if (i = t[r], ft.test(i)) { + if (delete t[r], o = o || "toggle" === i, i === (g ? "hide" : "show")) { + if ("show" !== i || !v || void 0 === v[r]) continue; + g = !0 + } + d[r] = v && v[r] || ce.style(e, r) + } if ((u = !ce.isEmptyObject(t)) || !ce.isEmptyObject(d)) + for (r in f && 1 === e.nodeType && (n.overflow = [h.overflow, h.overflowX, h.overflowY], null == (l = v && v.display) && (l = _.get(e, "display")), "none" === (c = ce.css(e, "display")) && (l ? c = l : (re([e], !0), l = e.style.display || l, c = ce.css(e, "display"), re([e]))), ("inline" === c || "inline-block" === c && null != l) && "none" === ce.css(e, "float") && (u || (p.done(function() { + h.display = l + }), null == l && (c = h.display, l = "none" === c ? "" : c)), h.display = "inline-block")), n.overflow && (h.overflow = "hidden", p.always(function() { + h.overflow = n.overflow[0], h.overflowX = n.overflow[1], h.overflowY = n.overflow[2] + })), u = !1, d) u || (v ? "hidden" in v && (g = v.hidden) : v = _.access(e, "fxshow", { + display: l + }), o && (v.hidden = !g), g && re([e], !0), p.done(function() { + for (r in g || re([e]), _.remove(e, "fxshow"), d) ce.style(e, r, d[r]) + })), u = vt(g ? v[r] : 0, r, p), r in v || (v[r] = u.start, g && (u.end = u.start, u.start = 0)) + }], + prefilter: function(e, t) { + t ? yt.prefilters.unshift(e) : yt.prefilters.push(e) + } + }), ce.speed = function(e, t, n) { + var r = e && "object" == typeof e ? ce.extend({}, e) : { + complete: n || !n && t || v(e) && e, + duration: e, + easing: n && t || t && !v(t) && t + }; + return ce.fx.off ? r.duration = 0 : "number" != typeof r.duration && (r.duration in ce.fx.speeds ? r.duration = ce.fx.speeds[r.duration] : r.duration = ce.fx.speeds._default), null != r.queue && !0 !== r.queue || (r.queue = "fx"), r.old = r.complete, r.complete = function() { + v(r.old) && r.old.call(this), r.queue && ce.dequeue(this, r.queue) + }, r + }, ce.fn.extend({ + fadeTo: function(e, t, n, r) { + return this.filter(ee).css("opacity", 0).show().end().animate({ + opacity: t + }, e, n, r) + }, + animate: function(t, e, n, r) { + var i = ce.isEmptyObject(t), + o = ce.speed(e, n, r), + a = function() { + var e = yt(this, ce.extend({}, t), o); + (i || _.get(this, "finish")) && e.stop(!0) + }; + return a.finish = a, i || !1 === o.queue ? this.each(a) : this.queue(o.queue, a) + }, + stop: function(i, e, o) { + var a = function(e) { + var t = e.stop; + delete e.stop, t(o) + }; + return "string" != typeof i && (o = e, e = i, i = void 0), e && this.queue(i || "fx", []), this.each(function() { + var e = !0, + t = null != i && i + "queueHooks", + n = ce.timers, + r = _.get(this); + if (t) r[t] && r[t].stop && a(r[t]); + else + for (t in r) r[t] && r[t].stop && pt.test(t) && a(r[t]); + for (t = n.length; t--;) n[t].elem !== this || null != i && n[t].queue !== i || (n[t].anim.stop(o), e = !1, n.splice(t, 1)); + !e && o || ce.dequeue(this, i) + }) + }, + finish: function(a) { + return !1 !== a && (a = a || "fx"), this.each(function() { + var e, t = _.get(this), + n = t[a + "queue"], + r = t[a + "queueHooks"], + i = ce.timers, + o = n ? n.length : 0; + for (t.finish = !0, ce.queue(this, a, []), r && r.stop && r.stop.call(this, !0), e = i.length; e--;) i[e].elem === this && i[e].queue === a && (i[e].anim.stop(!0), i.splice(e, 1)); + for (e = 0; e < o; e++) n[e] && n[e].finish && n[e].finish.call(this); + delete t.finish + }) + } + }), ce.each(["toggle", "show", "hide"], function(e, r) { + var i = ce.fn[r]; + ce.fn[r] = function(e, t, n) { + return null == e || "boolean" == typeof e ? i.apply(this, arguments) : this.animate(gt(r, !0), e, t, n) + } + }), ce.each({ + slideDown: gt("show"), + slideUp: gt("hide"), + slideToggle: gt("toggle"), + fadeIn: { + opacity: "show" + }, + fadeOut: { + opacity: "hide" + }, + fadeToggle: { + opacity: "toggle" + } + }, function(e, r) { + ce.fn[e] = function(e, t, n) { + return this.animate(r, e, t, n) + } + }), ce.timers = [], ce.fx.tick = function() { + var e, t = 0, + n = ce.timers; + for (st = Date.now(); t < n.length; t++)(e = n[t])() || n[t] !== e || n.splice(t--, 1); + n.length || ce.fx.stop(), st = void 0 + }, ce.fx.timer = function(e) { + ce.timers.push(e), ce.fx.start() + }, ce.fx.interval = 13, ce.fx.start = function() { + ut || (ut = !0, dt()) + }, ce.fx.stop = function() { + ut = null + }, ce.fx.speeds = { + slow: 600, + fast: 200, + _default: 400 + }, ce.fn.delay = function(r, e) { + return r = ce.fx && ce.fx.speeds[r] || r, e = e || "fx", this.queue(e, function(e, t) { + var n = ie.setTimeout(e, r); + t.stop = function() { + ie.clearTimeout(n) + } + }) + }, lt = C.createElement("input"), ct = C.createElement("select").appendChild(C.createElement("option")), lt.type = "checkbox", le.checkOn = "" !== lt.value, le.optSelected = ct.selected, (lt = C.createElement("input")).value = "t", lt.type = "radio", le.radioValue = "t" === lt.value; + var mt, xt = ce.expr.attrHandle; + ce.fn.extend({ + attr: function(e, t) { + return M(this, ce.attr, e, t, 1 < arguments.length) + }, + removeAttr: function(e) { + return this.each(function() { + ce.removeAttr(this, e) + }) + } + }), ce.extend({ + attr: function(e, t, n) { + var r, i, o = e.nodeType; + if (3 !== o && 8 !== o && 2 !== o) return "undefined" == typeof e.getAttribute ? ce.prop(e, t, n) : (1 === o && ce.isXMLDoc(e) || (i = ce.attrHooks[t.toLowerCase()] || (ce.expr.match.bool.test(t) ? mt : void 0)), void 0 !== n ? null === n ? void ce.removeAttr(e, t) : i && "set" in i && void 0 !== (r = i.set(e, n, t)) ? r : (e.setAttribute(t, n + ""), n) : i && "get" in i && null !== (r = i.get(e, t)) ? r : null == (r = ce.find.attr(e, t)) ? void 0 : r) + }, + attrHooks: { + type: { + set: function(e, t) { + if (!le.radioValue && "radio" === t && fe(e, "input")) { + var n = e.value; + return e.setAttribute("type", t), n && (e.value = n), t + } + } + } + }, + removeAttr: function(e, t) { + var n, r = 0, + i = t && t.match(D); + if (i && 1 === e.nodeType) + while (n = i[r++]) e.removeAttribute(n) + } + }), mt = { + set: function(e, t, n) { + return !1 === t ? ce.removeAttr(e, n) : e.setAttribute(n, n), n + } + }, ce.each(ce.expr.match.bool.source.match(/\w+/g), function(e, t) { + var a = xt[t] || ce.find.attr; + xt[t] = function(e, t, n) { + var r, i, o = t.toLowerCase(); + return n || (i = xt[o], xt[o] = r, r = null != a(e, t, n) ? o : null, xt[o] = i), r + } + }); + var bt = /^(?:input|select|textarea|button)$/i, + wt = /^(?:a|area)$/i; + + function Tt(e) { + return (e.match(D) || []).join(" ") + } + + function Ct(e) { + return e.getAttribute && e.getAttribute("class") || "" + } + + function kt(e) { + return Array.isArray(e) ? e : "string" == typeof e && e.match(D) || [] + } + ce.fn.extend({ + prop: function(e, t) { + return M(this, ce.prop, e, t, 1 < arguments.length) + }, + removeProp: function(e) { + return this.each(function() { + delete this[ce.propFix[e] || e] + }) + } + }), ce.extend({ + prop: function(e, t, n) { + var r, i, o = e.nodeType; + if (3 !== o && 8 !== o && 2 !== o) return 1 === o && ce.isXMLDoc(e) || (t = ce.propFix[t] || t, i = ce.propHooks[t]), void 0 !== n ? i && "set" in i && void 0 !== (r = i.set(e, n, t)) ? r : e[t] = n : i && "get" in i && null !== (r = i.get(e, t)) ? r : e[t] + }, + propHooks: { + tabIndex: { + get: function(e) { + var t = ce.find.attr(e, "tabindex"); + return t ? parseInt(t, 10) : bt.test(e.nodeName) || wt.test(e.nodeName) && e.href ? 0 : -1 + } + } + }, + propFix: { + "for": "htmlFor", + "class": "className" + } + }), le.optSelected || (ce.propHooks.selected = { + get: function(e) { + var t = e.parentNode; + return t && t.parentNode && t.parentNode.selectedIndex, null + }, + set: function(e) { + var t = e.parentNode; + t && (t.selectedIndex, t.parentNode && t.parentNode.selectedIndex) + } + }), ce.each(["tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable"], function() { + ce.propFix[this.toLowerCase()] = this + }), ce.fn.extend({ + addClass: function(t) { + var e, n, r, i, o, a; + return v(t) ? this.each(function(e) { + ce(this).addClass(t.call(this, e, Ct(this))) + }) : (e = kt(t)).length ? this.each(function() { + if (r = Ct(this), n = 1 === this.nodeType && " " + Tt(r) + " ") { + for (o = 0; o < e.length; o++) i = e[o], n.indexOf(" " + i + " ") < 0 && (n += i + " "); + a = Tt(n), r !== a && this.setAttribute("class", a) + } + }) : this + }, + removeClass: function(t) { + var e, n, r, i, o, a; + return v(t) ? this.each(function(e) { + ce(this).removeClass(t.call(this, e, Ct(this))) + }) : arguments.length ? (e = kt(t)).length ? this.each(function() { + if (r = Ct(this), n = 1 === this.nodeType && " " + Tt(r) + " ") { + for (o = 0; o < e.length; o++) { + i = e[o]; + while (-1 < n.indexOf(" " + i + " ")) n = n.replace(" " + i + " ", " ") + } + a = Tt(n), r !== a && this.setAttribute("class", a) + } + }) : this : this.attr("class", "") + }, + toggleClass: function(t, n) { + var e, r, i, o, a = typeof t, + s = "string" === a || Array.isArray(t); + return v(t) ? this.each(function(e) { + ce(this).toggleClass(t.call(this, e, Ct(this), n), n) + }) : "boolean" == typeof n && s ? n ? this.addClass(t) : this.removeClass(t) : (e = kt(t), this.each(function() { + if (s) + for (o = ce(this), i = 0; i < e.length; i++) r = e[i], o.hasClass(r) ? o.removeClass(r) : o.addClass(r); + else void 0 !== t && "boolean" !== a || ((r = Ct(this)) && _.set(this, "__className__", r), this.setAttribute && this.setAttribute("class", r || !1 === t ? "" : _.get(this, "__className__") || "")) + })) + }, + hasClass: function(e) { + var t, n, r = 0; + t = " " + e + " "; + while (n = this[r++]) + if (1 === n.nodeType && -1 < (" " + Tt(Ct(n)) + " ").indexOf(t)) return !0; + return !1 + } + }); + var St = /\r/g; + ce.fn.extend({ + val: function(n) { + var r, e, i, t = this[0]; + return arguments.length ? (i = v(n), this.each(function(e) { + var t; + 1 === this.nodeType && (null == (t = i ? n.call(this, e, ce(this).val()) : n) ? t = "" : "number" == typeof t ? t += "" : Array.isArray(t) && (t = ce.map(t, function(e) { + return null == e ? "" : e + "" + })), (r = ce.valHooks[this.type] || ce.valHooks[this.nodeName.toLowerCase()]) && "set" in r && void 0 !== r.set(this, t, "value") || (this.value = t)) + })) : t ? (r = ce.valHooks[t.type] || ce.valHooks[t.nodeName.toLowerCase()]) && "get" in r && void 0 !== (e = r.get(t, "value")) ? e : "string" == typeof(e = t.value) ? e.replace(St, "") : null == e ? "" : e : void 0 + } + }), ce.extend({ + valHooks: { + option: { + get: function(e) { + var t = ce.find.attr(e, "value"); + return null != t ? t : Tt(ce.text(e)) + } + }, + select: { + get: function(e) { + var t, n, r, i = e.options, + o = e.selectedIndex, + a = "select-one" === e.type, + s = a ? null : [], + u = a ? o + 1 : i.length; + for (r = o < 0 ? u : a ? o : 0; r < u; r++) + if (((n = i[r]).selected || r === o) && !n.disabled && (!n.parentNode.disabled || !fe(n.parentNode, "optgroup"))) { + if (t = ce(n).val(), a) return t; + s.push(t) + } return s + }, + set: function(e, t) { + var n, r, i = e.options, + o = ce.makeArray(t), + a = i.length; + while (a--)((r = i[a]).selected = -1 < ce.inArray(ce.valHooks.option.get(r), o)) && (n = !0); + return n || (e.selectedIndex = -1), o + } + } + } + }), ce.each(["radio", "checkbox"], function() { + ce.valHooks[this] = { + set: function(e, t) { + if (Array.isArray(t)) return e.checked = -1 < ce.inArray(ce(e).val(), t) + } + }, le.checkOn || (ce.valHooks[this].get = function(e) { + return null === e.getAttribute("value") ? "on" : e.value + }) + }); + var Et = ie.location, + jt = { + guid: Date.now() + }, + At = /\?/; + ce.parseXML = function(e) { + var t, n; + if (!e || "string" != typeof e) return null; + try { + t = (new ie.DOMParser).parseFromString(e, "text/xml") + } catch (e) {} + return n = t && t.getElementsByTagName("parsererror")[0], t && !n || ce.error("Invalid XML: " + (n ? ce.map(n.childNodes, function(e) { + return e.textContent + }).join("\n") : e)), t + }; + var Dt = /^(?:focusinfocus|focusoutblur)$/, + Nt = function(e) { + e.stopPropagation() + }; + ce.extend(ce.event, { + trigger: function(e, t, n, r) { + var i, o, a, s, u, l, c, f, p = [n || C], + d = ue.call(e, "type") ? e.type : e, + h = ue.call(e, "namespace") ? e.namespace.split(".") : []; + if (o = f = a = n = n || C, 3 !== n.nodeType && 8 !== n.nodeType && !Dt.test(d + ce.event.triggered) && (-1 < d.indexOf(".") && (d = (h = d.split(".")).shift(), h.sort()), u = d.indexOf(":") < 0 && "on" + d, (e = e[ce.expando] ? e : new ce.Event(d, "object" == typeof e && e)).isTrigger = r ? 2 : 3, e.namespace = h.join("."), e.rnamespace = e.namespace ? new RegExp("(^|\\.)" + h.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, e.result = void 0, e.target || (e.target = n), t = null == t ? [e] : ce.makeArray(t, [e]), c = ce.event.special[d] || {}, r || !c.trigger || !1 !== c.trigger.apply(n, t))) { + if (!r && !c.noBubble && !y(n)) { + for (s = c.delegateType || d, Dt.test(s + d) || (o = o.parentNode); o; o = o.parentNode) p.push(o), a = o; + a === (n.ownerDocument || C) && p.push(a.defaultView || a.parentWindow || ie) + } + i = 0; + while ((o = p[i++]) && !e.isPropagationStopped()) f = o, e.type = 1 < i ? s : c.bindType || d, (l = (_.get(o, "events") || Object.create(null))[e.type] && _.get(o, "handle")) && l.apply(o, t), (l = u && o[u]) && l.apply && $(o) && (e.result = l.apply(o, t), !1 === e.result && e.preventDefault()); + return e.type = d, r || e.isDefaultPrevented() || c._default && !1 !== c._default.apply(p.pop(), t) || !$(n) || u && v(n[d]) && !y(n) && ((a = n[u]) && (n[u] = null), ce.event.triggered = d, e.isPropagationStopped() && f.addEventListener(d, Nt), n[d](), e.isPropagationStopped() && f.removeEventListener(d, Nt), ce.event.triggered = void 0, a && (n[u] = a)), e.result + } + }, + simulate: function(e, t, n) { + var r = ce.extend(new ce.Event, n, { + type: e, + isSimulated: !0 + }); + ce.event.trigger(r, null, t) + } + }), ce.fn.extend({ + trigger: function(e, t) { + return this.each(function() { + ce.event.trigger(e, t, this) + }) + }, + triggerHandler: function(e, t) { + var n = this[0]; + if (n) return ce.event.trigger(e, t, n, !0) + } + }); + var qt = /\[\]$/, + Lt = /\r?\n/g, + Ht = /^(?:submit|button|image|reset|file)$/i, + Ot = /^(?:input|select|textarea|keygen)/i; + + function Pt(n, e, r, i) { + var t; + if (Array.isArray(e)) ce.each(e, function(e, t) { + r || qt.test(n) ? i(n, t) : Pt(n + "[" + ("object" == typeof t && null != t ? e : "") + "]", t, r, i) + }); + else if (r || "object" !== x(e)) i(n, e); + else + for (t in e) Pt(n + "[" + t + "]", e[t], r, i) + } + ce.param = function(e, t) { + var n, r = [], + i = function(e, t) { + var n = v(t) ? t() : t; + r[r.length] = encodeURIComponent(e) + "=" + encodeURIComponent(null == n ? "" : n) + }; + if (null == e) return ""; + if (Array.isArray(e) || e.jquery && !ce.isPlainObject(e)) ce.each(e, function() { + i(this.name, this.value) + }); + else + for (n in e) Pt(n, e[n], t, i); + return r.join("&") + }, ce.fn.extend({ + serialize: function() { + return ce.param(this.serializeArray()) + }, + serializeArray: function() { + return this.map(function() { + var e = ce.prop(this, "elements"); + return e ? ce.makeArray(e) : this + }).filter(function() { + var e = this.type; + return this.name && !ce(this).is(":disabled") && Ot.test(this.nodeName) && !Ht.test(e) && (this.checked || !we.test(e)) + }).map(function(e, t) { + var n = ce(this).val(); + return null == n ? null : Array.isArray(n) ? ce.map(n, function(e) { + return { + name: t.name, + value: e.replace(Lt, "\r\n") + } + }) : { + name: t.name, + value: n.replace(Lt, "\r\n") + } + }).get() + } + }); + var Mt = /%20/g, + Rt = /#.*$/, + It = /([?&])_=[^&]*/, + Wt = /^(.*?):[ \t]*([^\r\n]*)$/gm, + Ft = /^(?:GET|HEAD)$/, + $t = /^\/\//, + Bt = {}, + _t = {}, + zt = "*/".concat("*"), + Xt = C.createElement("a"); + + function Ut(o) { + return function(e, t) { + "string" != typeof e && (t = e, e = "*"); + var n, r = 0, + i = e.toLowerCase().match(D) || []; + if (v(t)) + while (n = i[r++]) "+" === n[0] ? (n = n.slice(1) || "*", (o[n] = o[n] || []).unshift(t)) : (o[n] = o[n] || []).push(t) + } + } + + function Vt(t, i, o, a) { + var s = {}, + u = t === _t; + + function l(e) { + var r; + return s[e] = !0, ce.each(t[e] || [], function(e, t) { + var n = t(i, o, a); + return "string" != typeof n || u || s[n] ? u ? !(r = n) : void 0 : (i.dataTypes.unshift(n), l(n), !1) + }), r + } + return l(i.dataTypes[0]) || !s["*"] && l("*") + } + + function Gt(e, t) { + var n, r, i = ce.ajaxSettings.flatOptions || {}; + for (n in t) void 0 !== t[n] && ((i[n] ? e : r || (r = {}))[n] = t[n]); + return r && ce.extend(!0, e, r), e + } + Xt.href = Et.href, ce.extend({ + active: 0, + lastModified: {}, + etag: {}, + ajaxSettings: { + url: Et.href, + type: "GET", + isLocal: /^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol), + global: !0, + processData: !0, + async: !0, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + accepts: { + "*": zt, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + converters: { + "* text": String, + "text html": !0, + "text json": JSON.parse, + "text xml": ce.parseXML + }, + flatOptions: { + url: !0, + context: !0 + } + }, + ajaxSetup: function(e, t) { + return t ? Gt(Gt(e, ce.ajaxSettings), t) : Gt(ce.ajaxSettings, e) + }, + ajaxPrefilter: Ut(Bt), + ajaxTransport: Ut(_t), + ajax: function(e, t) { + "object" == typeof e && (t = e, e = void 0), t = t || {}; + var c, f, p, n, d, r, h, g, i, o, v = ce.ajaxSetup({}, t), + y = v.context || v, + m = v.context && (y.nodeType || y.jquery) ? ce(y) : ce.event, + x = ce.Deferred(), + b = ce.Callbacks("once memory"), + w = v.statusCode || {}, + a = {}, + s = {}, + u = "canceled", + T = { + readyState: 0, + getResponseHeader: function(e) { + var t; + if (h) { + if (!n) { + n = {}; + while (t = Wt.exec(p)) n[t[1].toLowerCase() + " "] = (n[t[1].toLowerCase() + " "] || []).concat(t[2]) + } + t = n[e.toLowerCase() + " "] + } + return null == t ? null : t.join(", ") + }, + getAllResponseHeaders: function() { + return h ? p : null + }, + setRequestHeader: function(e, t) { + return null == h && (e = s[e.toLowerCase()] = s[e.toLowerCase()] || e, a[e] = t), this + }, + overrideMimeType: function(e) { + return null == h && (v.mimeType = e), this + }, + statusCode: function(e) { + var t; + if (e) + if (h) T.always(e[T.status]); + else + for (t in e) w[t] = [w[t], e[t]]; + return this + }, + abort: function(e) { + var t = e || u; + return c && c.abort(t), l(0, t), this + } + }; + if (x.promise(T), v.url = ((e || v.url || Et.href) + "").replace($t, Et.protocol + "//"), v.type = t.method || t.type || v.method || v.type, v.dataTypes = (v.dataType || "*").toLowerCase().match(D) || [""], null == v.crossDomain) { + r = C.createElement("a"); + try { + r.href = v.url, r.href = r.href, v.crossDomain = Xt.protocol + "//" + Xt.host != r.protocol + "//" + r.host + } catch (e) { + v.crossDomain = !0 + } + } + if (v.data && v.processData && "string" != typeof v.data && (v.data = ce.param(v.data, v.traditional)), Vt(Bt, v, t, T), h) return T; + for (i in (g = ce.event && v.global) && 0 == ce.active++ && ce.event.trigger("ajaxStart"), v.type = v.type.toUpperCase(), v.hasContent = !Ft.test(v.type), f = v.url.replace(Rt, ""), v.hasContent ? v.data && v.processData && 0 === (v.contentType || "").indexOf("application/x-www-form-urlencoded") && (v.data = v.data.replace(Mt, "+")) : (o = v.url.slice(f.length), v.data && (v.processData || "string" == typeof v.data) && (f += (At.test(f) ? "&" : "?") + v.data, delete v.data), !1 === v.cache && (f = f.replace(It, "$1"), o = (At.test(f) ? "&" : "?") + "_=" + jt.guid++ + o), v.url = f + o), v.ifModified && (ce.lastModified[f] && T.setRequestHeader("If-Modified-Since", ce.lastModified[f]), ce.etag[f] && T.setRequestHeader("If-None-Match", ce.etag[f])), (v.data && v.hasContent && !1 !== v.contentType || t.contentType) && T.setRequestHeader("Content-Type", v.contentType), T.setRequestHeader("Accept", v.dataTypes[0] && v.accepts[v.dataTypes[0]] ? v.accepts[v.dataTypes[0]] + ("*" !== v.dataTypes[0] ? ", " + zt + "; q=0.01" : "") : v.accepts["*"]), v.headers) T.setRequestHeader(i, v.headers[i]); + if (v.beforeSend && (!1 === v.beforeSend.call(y, T, v) || h)) return T.abort(); + if (u = "abort", b.add(v.complete), T.done(v.success), T.fail(v.error), c = Vt(_t, v, t, T)) { + if (T.readyState = 1, g && m.trigger("ajaxSend", [T, v]), h) return T; + v.async && 0 < v.timeout && (d = ie.setTimeout(function() { + T.abort("timeout") + }, v.timeout)); + try { + h = !1, c.send(a, l) + } catch (e) { + if (h) throw e; + l(-1, e) + } + } else l(-1, "No Transport"); + + function l(e, t, n, r) { + var i, o, a, s, u, l = t; + h || (h = !0, d && ie.clearTimeout(d), c = void 0, p = r || "", T.readyState = 0 < e ? 4 : 0, i = 200 <= e && e < 300 || 304 === e, n && (s = function(e, t, n) { + var r, i, o, a, s = e.contents, + u = e.dataTypes; + while ("*" === u[0]) u.shift(), void 0 === r && (r = e.mimeType || t.getResponseHeader("Content-Type")); + if (r) + for (i in s) + if (s[i] && s[i].test(r)) { + u.unshift(i); + break + } if (u[0] in n) o = u[0]; + else { + for (i in n) { + if (!u[0] || e.converters[i + " " + u[0]]) { + o = i; + break + } + a || (a = i) + } + o = o || a + } + if (o) return o !== u[0] && u.unshift(o), n[o] + }(v, T, n)), !i && -1 < ce.inArray("script", v.dataTypes) && ce.inArray("json", v.dataTypes) < 0 && (v.converters["text script"] = function() {}), s = function(e, t, n, r) { + var i, o, a, s, u, l = {}, + c = e.dataTypes.slice(); + if (c[1]) + for (a in e.converters) l[a.toLowerCase()] = e.converters[a]; + o = c.shift(); + while (o) + if (e.responseFields[o] && (n[e.responseFields[o]] = t), !u && r && e.dataFilter && (t = e.dataFilter(t, e.dataType)), u = o, o = c.shift()) + if ("*" === o) o = u; + else if ("*" !== u && u !== o) { + if (!(a = l[u + " " + o] || l["* " + o])) + for (i in l) + if ((s = i.split(" "))[1] === o && (a = l[u + " " + s[0]] || l["* " + s[0]])) { + !0 === a ? a = l[i] : !0 !== l[i] && (o = s[0], c.unshift(s[1])); + break + } if (!0 !== a) + if (a && e["throws"]) t = a(t); + else try { + t = a(t) + } catch (e) { + return { + state: "parsererror", + error: a ? e : "No conversion from " + u + " to " + o + } + } + } + return { + state: "success", + data: t + } + }(v, s, T, i), i ? (v.ifModified && ((u = T.getResponseHeader("Last-Modified")) && (ce.lastModified[f] = u), (u = T.getResponseHeader("etag")) && (ce.etag[f] = u)), 204 === e || "HEAD" === v.type ? l = "nocontent" : 304 === e ? l = "notmodified" : (l = s.state, o = s.data, i = !(a = s.error))) : (a = l, !e && l || (l = "error", e < 0 && (e = 0))), T.status = e, T.statusText = (t || l) + "", i ? x.resolveWith(y, [o, l, T]) : x.rejectWith(y, [T, l, a]), T.statusCode(w), w = void 0, g && m.trigger(i ? "ajaxSuccess" : "ajaxError", [T, v, i ? o : a]), b.fireWith(y, [T, l]), g && (m.trigger("ajaxComplete", [T, v]), --ce.active || ce.event.trigger("ajaxStop"))) + } + return T + }, + getJSON: function(e, t, n) { + return ce.get(e, t, n, "json") + }, + getScript: function(e, t) { + return ce.get(e, void 0, t, "script") + } + }), ce.each(["get", "post"], function(e, i) { + ce[i] = function(e, t, n, r) { + return v(t) && (r = r || n, n = t, t = void 0), ce.ajax(ce.extend({ + url: e, + type: i, + dataType: r, + data: t, + success: n + }, ce.isPlainObject(e) && e)) + } + }), ce.ajaxPrefilter(function(e) { + var t; + for (t in e.headers) "content-type" === t.toLowerCase() && (e.contentType = e.headers[t] || "") + }), ce._evalUrl = function(e, t, n) { + return ce.ajax({ + url: e, + type: "GET", + dataType: "script", + cache: !0, + async: !1, + global: !1, + converters: { + "text script": function() {} + }, + dataFilter: function(e) { + ce.globalEval(e, t, n) + } + }) + }, ce.fn.extend({ + wrapAll: function(e) { + var t; + return this[0] && (v(e) && (e = e.call(this[0])), t = ce(e, this[0].ownerDocument).eq(0).clone(!0), this[0].parentNode && t.insertBefore(this[0]), t.map(function() { + var e = this; + while (e.firstElementChild) e = e.firstElementChild; + return e + }).append(this)), this + }, + wrapInner: function(n) { + return v(n) ? this.each(function(e) { + ce(this).wrapInner(n.call(this, e)) + }) : this.each(function() { + var e = ce(this), + t = e.contents(); + t.length ? t.wrapAll(n) : e.append(n) + }) + }, + wrap: function(t) { + var n = v(t); + return this.each(function(e) { + ce(this).wrapAll(n ? t.call(this, e) : t) + }) + }, + unwrap: function(e) { + return this.parent(e).not("body").each(function() { + ce(this).replaceWith(this.childNodes) + }), this + } + }), ce.expr.pseudos.hidden = function(e) { + return !ce.expr.pseudos.visible(e) + }, ce.expr.pseudos.visible = function(e) { + return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length) + }, ce.ajaxSettings.xhr = function() { + try { + return new ie.XMLHttpRequest + } catch (e) {} + }; + var Yt = { + 0: 200, + 1223: 204 + }, + Qt = ce.ajaxSettings.xhr(); + le.cors = !!Qt && "withCredentials" in Qt, le.ajax = Qt = !!Qt, ce.ajaxTransport(function(i) { + var o, a; + if (le.cors || Qt && !i.crossDomain) return { + send: function(e, t) { + var n, r = i.xhr(); + if (r.open(i.type, i.url, i.async, i.username, i.password), i.xhrFields) + for (n in i.xhrFields) r[n] = i.xhrFields[n]; + for (n in i.mimeType && r.overrideMimeType && r.overrideMimeType(i.mimeType), i.crossDomain || e["X-Requested-With"] || (e["X-Requested-With"] = "XMLHttpRequest"), e) r.setRequestHeader(n, e[n]); + o = function(e) { + return function() { + o && (o = a = r.onload = r.onerror = r.onabort = r.ontimeout = r.onreadystatechange = null, "abort" === e ? r.abort() : "error" === e ? "number" != typeof r.status ? t(0, "error") : t(r.status, r.statusText) : t(Yt[r.status] || r.status, r.statusText, "text" !== (r.responseType || "text") || "string" != typeof r.responseText ? { + binary: r.response + } : { + text: r.responseText + }, r.getAllResponseHeaders())) + } + }, r.onload = o(), a = r.onerror = r.ontimeout = o("error"), void 0 !== r.onabort ? r.onabort = a : r.onreadystatechange = function() { + 4 === r.readyState && ie.setTimeout(function() { + o && a() + }) + }, o = o("abort"); + try { + r.send(i.hasContent && i.data || null) + } catch (e) { + if (o) throw e + } + }, + abort: function() { + o && o() + } + } + }), ce.ajaxPrefilter(function(e) { + e.crossDomain && (e.contents.script = !1) + }), ce.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function(e) { + return ce.globalEval(e), e + } + } + }), ce.ajaxPrefilter("script", function(e) { + void 0 === e.cache && (e.cache = !1), e.crossDomain && (e.type = "GET") + }), ce.ajaxTransport("script", function(n) { + var r, i; + if (n.crossDomain || n.scriptAttrs) return { + send: function(e, t) { + r = ce(" +{% endblock %} diff --git a/adaptive_hockey_federation/templates/analytics/dashboard.html b/adaptive_hockey_federation/templates/analytics/dashboard.html new file mode 100644 index 00000000..c1c7656c --- /dev/null +++ b/adaptive_hockey_federation/templates/analytics/dashboard.html @@ -0,0 +1,46 @@ +{% load static %} +
+
+
+ {% for metric in dashboard.primary %} +
+

+ {{ metric.1 }} +

+

+ {{ metric.0|title }} +

+
+ {% endfor %} +
+
+ {% for metric in dashboard.secondary %} +
+

+ {{ metric.1|title }} +

+

+ {{ metric.0 }} +

+
+ {% endfor %} +
+
+ {% for metric in dashboard.nosology %} +
+

+ {{ metric.1|title }} +

+

+ {{ metric.0|truncatechars:10 }} +

+
+ {% endfor %} +
+
+
diff --git a/adaptive_hockey_federation/templates/analytics/filters.html b/adaptive_hockey_federation/templates/analytics/filters.html new file mode 100644 index 00000000..544edc73 --- /dev/null +++ b/adaptive_hockey_federation/templates/analytics/filters.html @@ -0,0 +1,11 @@ +
+
+ {% for field in form %} +
+ {{ field.label }} + {{ field }} +
+ {% endfor %} +
+ +
diff --git a/adaptive_hockey_federation/templates/analytics/table.html b/adaptive_hockey_federation/templates/analytics/table.html new file mode 100644 index 00000000..663de800 --- /dev/null +++ b/adaptive_hockey_federation/templates/analytics/table.html @@ -0,0 +1,46 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk'%} + + {% endif %} + {% endfor %} + + {% endfor %} + +
{{ head_item }}
+ {{ cell_value }} + + {% include 'main/players/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + + {% if cell_name == 'team' %} + {% for team in cell_value %} + {{ team }} +
+ {% endfor %} + {% else %} + {{ cell_value }} + {% endif %} +
+
diff --git a/adaptive_hockey_federation/templates/base/active_check_icon.html b/adaptive_hockey_federation/templates/base/active_check_icon.html new file mode 100644 index 00000000..d32196f2 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/active_check_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/active_delete_icon.html b/adaptive_hockey_federation/templates/base/active_delete_icon.html new file mode 100644 index 00000000..33f5bd3c --- /dev/null +++ b/adaptive_hockey_federation/templates/base/active_delete_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/active_edit_icon.html b/adaptive_hockey_federation/templates/base/active_edit_icon.html new file mode 100644 index 00000000..429c8d18 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/active_edit_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/base.html b/adaptive_hockey_federation/templates/base/base.html new file mode 100644 index 00000000..16a62c63 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/base.html @@ -0,0 +1,86 @@ + + + + {% load static %} + + + + + + + + + + {% block title %} + Тут будет заголовок + {% endblock %} + + + + + + +
+ {% include 'base/drawer.html' %} +
+
+ {% block header %} + {% include 'base/header.html' %} + {% endblock %} +
+
+
+ {% block content_navigation %} + {% endblock %} + {% block content %} + Основной текст + {% endblock %} +
+
+ {% block footer %}{% include 'base/footer.html' %}{% endblock %} +
+
+ + + + + + + + + + + + {% block JavaScript %} + {% endblock %} + + + diff --git a/adaptive_hockey_federation/templates/base/base_edit_delete_buttons!.html b/adaptive_hockey_federation/templates/base/base_edit_delete_buttons!.html new file mode 100644 index 00000000..b86f50e7 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/base_edit_delete_buttons!.html @@ -0,0 +1,18 @@ +{% load static %} + +
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/base/button.html b/adaptive_hockey_federation/templates/base/button.html new file mode 100644 index 00000000..16fc232b --- /dev/null +++ b/adaptive_hockey_federation/templates/base/button.html @@ -0,0 +1,11 @@ +{% with request.path as current_url_name %} + + {{ name }} + +{% endwith %} diff --git a/adaptive_hockey_federation/templates/base/button_create.html b/adaptive_hockey_federation/templates/base/button_create.html new file mode 100644 index 00000000..615e5939 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/button_create.html @@ -0,0 +1,11 @@ +{% with request.path as current_url_name %} + + {{ name }} + +{% endwith %} diff --git a/adaptive_hockey_federation/templates/base/button_list.html b/adaptive_hockey_federation/templates/base/button_list.html new file mode 100644 index 00000000..4ca700b9 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/button_list.html @@ -0,0 +1,9 @@ + diff --git a/adaptive_hockey_federation/templates/base/cell_button.html b/adaptive_hockey_federation/templates/base/cell_button.html new file mode 100644 index 00000000..7126b4ec --- /dev/null +++ b/adaptive_hockey_federation/templates/base/cell_button.html @@ -0,0 +1,4 @@ +{{ name }} + diff --git a/adaptive_hockey_federation/templates/base/check_icon.html b/adaptive_hockey_federation/templates/base/check_icon.html new file mode 100644 index 00000000..7f6f460b --- /dev/null +++ b/adaptive_hockey_federation/templates/base/check_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/competition_button_create.html b/adaptive_hockey_federation/templates/base/competition_button_create.html new file mode 100644 index 00000000..45e4ed05 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/competition_button_create.html @@ -0,0 +1,2 @@ +{% url 'competitions:competition_add' as competition_create_url %} +{% include 'base/button_create.html' with button_url=competition_create_url name="Создать соревнование" current_url_name=request.path %} diff --git a/adaptive_hockey_federation/templates/base/create_delete_button_players!.html b/adaptive_hockey_federation/templates/base/create_delete_button_players!.html new file mode 100644 index 00000000..535b9032 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/create_delete_button_players!.html @@ -0,0 +1,16 @@ +{% load static %} + +
+ + + +
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/base/delete_icon.html b/adaptive_hockey_federation/templates/base/delete_icon.html new file mode 100644 index 00000000..556d7e32 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/delete_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/drawer.html b/adaptive_hockey_federation/templates/base/drawer.html new file mode 100644 index 00000000..b09cf267 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/drawer.html @@ -0,0 +1,61 @@ +{% if not request.user.is_moderator %} + +{% endif %} diff --git a/adaptive_hockey_federation/templates/base/edit_delete_buttons!.html b/adaptive_hockey_federation/templates/base/edit_delete_buttons!.html new file mode 100644 index 00000000..863efacc --- /dev/null +++ b/adaptive_hockey_federation/templates/base/edit_delete_buttons!.html @@ -0,0 +1,9 @@ +
+
+ Редактировать +
+ {% csrf_token %} + +
+
+
diff --git a/adaptive_hockey_federation/templates/base/edit_icon.html b/adaptive_hockey_federation/templates/base/edit_icon.html new file mode 100644 index 00000000..c1db8d89 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/edit_icon.html @@ -0,0 +1,3 @@ + + + diff --git a/adaptive_hockey_federation/templates/base/footer.html b/adaptive_hockey_federation/templates/base/footer.html new file mode 100644 index 00000000..23605b67 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/footer.html @@ -0,0 +1,58 @@ +
+ +
diff --git a/adaptive_hockey_federation/templates/base/header.html b/adaptive_hockey_federation/templates/base/header.html new file mode 100644 index 00000000..7cfa46b7 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/header.html @@ -0,0 +1,21 @@ +{% load static %} + diff --git a/adaptive_hockey_federation/templates/base/messages.html b/adaptive_hockey_federation/templates/base/messages.html new file mode 100644 index 00000000..594c49e1 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/messages.html @@ -0,0 +1,12 @@ +{% if messages %} + {% for message in messages %} +
+
+ +
+
+ {% endfor %} +{% endif %} diff --git a/adaptive_hockey_federation/templates/base/pagination.html b/adaptive_hockey_federation/templates/base/pagination.html new file mode 100644 index 00000000..546d9493 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/pagination.html @@ -0,0 +1,38 @@ + diff --git a/adaptive_hockey_federation/templates/base/pagination_teams.html b/adaptive_hockey_federation/templates/base/pagination_teams.html new file mode 100644 index 00000000..68b0171c --- /dev/null +++ b/adaptive_hockey_federation/templates/base/pagination_teams.html @@ -0,0 +1,37 @@ + diff --git a/adaptive_hockey_federation/templates/base/return_button.html b/adaptive_hockey_federation/templates/base/return_button.html new file mode 100644 index 00000000..9e4c8ea9 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/return_button.html @@ -0,0 +1 @@ + diff --git a/adaptive_hockey_federation/templates/base/search_form.html b/adaptive_hockey_federation/templates/base/search_form.html new file mode 100644 index 00000000..9abf272e --- /dev/null +++ b/adaptive_hockey_federation/templates/base/search_form.html @@ -0,0 +1,64 @@ +
+
+ + + + + + + +
+
diff --git a/adaptive_hockey_federation/templates/base/team_button_create.html b/adaptive_hockey_federation/templates/base/team_button_create.html new file mode 100644 index 00000000..3c7ad9f4 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/team_button_create.html @@ -0,0 +1,4 @@ +{% if perms.main.add_team %} + {% url 'main:team_create' as team_create_url %} + {% include 'base/button_create.html' with button_url=team_create_url name="Создать команду" current_url_name=request.path %} +{% endif %} diff --git a/adaptive_hockey_federation/templates/base/team_edit_delete_buttons!.html b/adaptive_hockey_federation/templates/base/team_edit_delete_buttons!.html new file mode 100644 index 00000000..837ca758 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/team_edit_delete_buttons!.html @@ -0,0 +1 @@ +{% include "base/base_edit_delete_buttons.html" with action_url="main:team_update" delete_url="main:team_delete" is_activated=perms.team.change_team delete_message='Вы точно хотите удалить команду ' %} diff --git a/adaptive_hockey_federation/templates/base/triangle.html b/adaptive_hockey_federation/templates/base/triangle.html new file mode 100644 index 00000000..c61601b4 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/triangle.html @@ -0,0 +1,5 @@ +
+ + + +
diff --git a/adaptive_hockey_federation/templates/base/user_button_create!.html b/adaptive_hockey_federation/templates/base/user_button_create!.html new file mode 100644 index 00000000..b37e83d1 --- /dev/null +++ b/adaptive_hockey_federation/templates/base/user_button_create!.html @@ -0,0 +1,2 @@ +{% url 'users:user_create' as user_create_url %} +{% include "base/button_create.html" with button_url=user_create_url name="Создать пользователя" current_url_name=request.path %} diff --git a/adaptive_hockey_federation/templates/base/user_edit_delete_buttons!.html b/adaptive_hockey_federation/templates/base/user_edit_delete_buttons!.html new file mode 100644 index 00000000..a27dc66b --- /dev/null +++ b/adaptive_hockey_federation/templates/base/user_edit_delete_buttons!.html @@ -0,0 +1 @@ +{% include "base/base_edit_delete_buttons.html" with action_url="users:user_update" delete_url="users:user_delete" is_activated=perms.users.change_user delete_message='Вы точно хотите удалить пользователя ' %} diff --git a/adaptive_hockey_federation/templates/emailing/email.html b/adaptive_hockey_federation/templates/emailing/email.html new file mode 100644 index 00000000..e886b788 --- /dev/null +++ b/adaptive_hockey_federation/templates/emailing/email.html @@ -0,0 +1,377 @@ + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+ + + + + + + + + + +
+
+ {% block subject %} + {{subject}} + {% endblock %} +
+
+
+ {% block content %} + {{message|linebreaks}} + {% endblock %} +
+
+
+
+
+
+
+ + + + + + +
+
+ + + + + + +
+ + + + + + + +
+
+
+
+
+
+
+
+ + diff --git a/adaptive_hockey_federation/templates/emailing/password_reset_email.html b/adaptive_hockey_federation/templates/emailing/password_reset_email.html new file mode 100644 index 00000000..aeab7c57 --- /dev/null +++ b/adaptive_hockey_federation/templates/emailing/password_reset_email.html @@ -0,0 +1,13 @@ +{% extends "emailing/email.html" %} +{% block subject %}Доступ к админ-панели{% endblock %} +{% block content %} +
+ {% if message %} +

{{ message }}

+ {% else %} +

Пройдите по ссылке, чтобы получить доступ в личный кабинет.

+ {% endif %} + Пользователь: {{ user }} ({{ user.email }}) +

Ссылка

+
+{% endblock %} diff --git a/adaptive_hockey_federation/templates/emailing/welcome_letter.html b/adaptive_hockey_federation/templates/emailing/welcome_letter.html new file mode 100644 index 00000000..a55f82ea --- /dev/null +++ b/adaptive_hockey_federation/templates/emailing/welcome_letter.html @@ -0,0 +1,26 @@ +{% extends "emailing/email.html" %} +{% load static %} +{% block content %} +
+
+ +

Мир хоккея
для особенных
детей

+
+
+ Следж-хоккей, хоккей для незрячих, Специальный хоккей +
+
+
    +
  • Дата начала соревнования: {{ competition.date_start }}
  • +
  • Место проведения: город: {{ competition.city }}
  • +
  • Адрес: {{ competition.location }}
  • +
  • Надеемся на ваше активное участие и желаем удачи!
  • +
  • С уважением, организатор соревнований: {{ competition.title }}
  • +
+ +
+{% endblock %} diff --git a/adaptive_hockey_federation/templates/error-pages/403.html b/adaptive_hockey_federation/templates/error-pages/403.html new file mode 100644 index 00000000..6a02a55f --- /dev/null +++ b/adaptive_hockey_federation/templates/error-pages/403.html @@ -0,0 +1,4 @@ +{% extends "error-pages/base_error_page.html" %} +{% block title %}Отказано в доступе{% endblock %} +{% block error_heading %}Ошибка 403{% endblock %} +{% block error_paragraph %}Извините, похоже, у Вас нет полномочий для просмотра этой страницы.{% endblock %} diff --git a/adaptive_hockey_federation/templates/error-pages/404.html b/adaptive_hockey_federation/templates/error-pages/404.html new file mode 100644 index 00000000..2c6fcb26 --- /dev/null +++ b/adaptive_hockey_federation/templates/error-pages/404.html @@ -0,0 +1,4 @@ +{% extends "error-pages/base_error_page.html" %} +{% block title %}Не найдено{% endblock %} +{% block error_heading %}Ошибка 404{% endblock %} +{% block error_paragraph %}Извините, такой страницы не существует.{% endblock %} diff --git a/adaptive_hockey_federation/templates/error-pages/500.html b/adaptive_hockey_federation/templates/error-pages/500.html new file mode 100644 index 00000000..1973ce20 --- /dev/null +++ b/adaptive_hockey_federation/templates/error-pages/500.html @@ -0,0 +1,4 @@ +{% extends "error-pages/base_error_page.html" %} +{% block title %}Ошибка сервера{% endblock %} +{% block error_heading %}Ошибка 500{% endblock %} +{% block error_paragraph %}Извините, похоже, на сервере произошла ошибка.{% endblock %} diff --git a/adaptive_hockey_federation/templates/error-pages/base_error_page.html b/adaptive_hockey_federation/templates/error-pages/base_error_page.html new file mode 100644 index 00000000..a937d4d8 --- /dev/null +++ b/adaptive_hockey_federation/templates/error-pages/base_error_page.html @@ -0,0 +1,17 @@ +{% extends 'base/base.html' %} +{% block header %}{% endblock %} +{% block title %} + {{ title }} +{% endblock %} +{% block content %} +
+ {% load static %} + +
+

{% block error_heading %}{% endblock %}

+

{% block error_paragraph %}{% endblock %}

+ Вернуться на главную +
+
+{% endblock %} +{% block footer %}{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/competitions/base_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/competitions/base_edit_delete_buttons.html new file mode 100644 index 00000000..b4850e27 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/base_edit_delete_buttons.html @@ -0,0 +1,18 @@ + +{% load static %} + +
+
+ +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/competitions/competition_create_edit.html b/adaptive_hockey_federation/templates/main/competitions/competition_create_edit.html new file mode 100644 index 00000000..2e79e7df --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/competition_create_edit.html @@ -0,0 +1,49 @@ +{% extends 'base/base.html' %} +{% load user_filters %} +{% block title %} + {{ page_title }} +{% endblock %} +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

{{ object.name }}

+ + + {% for field in form.visible_fields %} + {% if field.name in 'title location'%} {% include "main/fields/text_input_field.html" %} + {% elif field.name in 'city'%} {% include "main/fields/city_field.html" %} + {% elif field.name in 'disciplines' %} {% include "main/fields/discipline_select_field.html" %} + {% elif field.name in 'date_start date_end'%} {% include "main/fields/text_input_field.html" %} + {% endif %} + {% endfor %} + +
+
+ +
+
+
+ +
+
+
+
+
+{% endblock %} +{% block JavaScript %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/competitions/competition_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/competitions/competition_edit_delete_buttons.html new file mode 100644 index 00000000..1e6ec49e --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/competition_edit_delete_buttons.html @@ -0,0 +1 @@ +{% include "main/competitions/base_edit_delete_buttons.html" with action_url="competitions:competition_update" delete_url="competitions:competition_delete" delete_message='Вы точно хотите удалить соревнование ' %} diff --git a/adaptive_hockey_federation/templates/main/competitions/competitions.html b/adaptive_hockey_federation/templates/main/competitions/competitions.html new file mode 100644 index 00000000..779428e3 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/competitions.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Список соревнований +{% endblock %} +{% block content %} + {% include "main/competitions/table.html" with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/competitions/competitions_team_search_field.html b/adaptive_hockey_federation/templates/main/competitions/competitions_team_search_field.html new file mode 100644 index 00000000..221077be --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/competitions_team_search_field.html @@ -0,0 +1,7 @@ +{% if field.name == 'team' %} + + {% for team in available_teams_list %} + + {% endfor %} + +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/competitions/table.html b/adaptive_hockey_federation/templates/main/competitions/table.html new file mode 100644 index 00000000..fc5a052e --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions/table.html @@ -0,0 +1,67 @@ +{% load static %} + + + + {% for head_item in table_head.values %} + + {% endfor %} + {% if not user.is_agent %} + + {% endif %} + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if cell_name == 'pk' %} + + {% elif cell_name == 'is_active' %} + + {% elif forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk'%} + + {% endif %} + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if not user.is_agent %} + {% if view_name == 'competitions:competitions' %} + {% include "main/competitions/competition_edit_delete_buttons.html" %} + {% else %} + + {% endif %} + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
{{ cell_value }} + {% if cell_value %} + {% include "base/active_check_icon.html" %} + {% else %} + {% include "base/check_icon.html" %} + {% endif %} + + {{ cell_value }} + + {% include 'base/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + {{ cell_value }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
diff --git a/adaptive_hockey_federation/templates/main/competitions_id/competitions_id.html b/adaptive_hockey_federation/templates/main/competitions_id/competitions_id.html new file mode 100644 index 00000000..ea88ca20 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions_id/competitions_id.html @@ -0,0 +1,50 @@ +{% extends "base/base.html" %} +{% load user_filters %} +{% block title %} + Профиль соревнования: {{ object.title }} +{% endblock %} +{% block content %} +

{{ object.title }}

+

Дата начала: {{ object.date_start }}

+

Дата окончания: {{ object.date_end }}

+

Город: {{ object.city }}

+

Место проведения: {{ object.location }}

+ {% if not user.is_agent %} +
+ {% csrf_token %} + {% for field in form %} +
+
+ + {{ field|addclass:'form-control' }} + {% include 'main/competitions/competitions_team_search_field.html' %} + {% if field.errors %} + {{ field.errors|striptags }} + {% endif %} +
+
+ {% endfor %} + +
+ {% endif %} + +

Команды, участвующие в событии:

+ {% include "main/competitions_id/components/competition_teams_table.html" %} + {% if not user.is_agent %} +

Доступные команды:

+ {% include "main/competitions_id/components/competitions_teams_table_available.html" with table_data=available_table_data %} + {% endif %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table.html b/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table.html new file mode 100644 index 00000000..6a8e9eac --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table.html @@ -0,0 +1,17 @@ +{% extends "main/competitions_id/components/competition_teams_table_base.html" %} +{% block action_button %} +
+ {% csrf_token %} + +
+{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table_base.html b/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table_base.html new file mode 100644 index 00000000..a70231b5 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions_id/components/competition_teams_table_base.html @@ -0,0 +1,31 @@ + + + + + + + + {% if not user.is_agent %} + + {% endif %} + + + + {% for team in table_data %} + + + + + + {% if not user.is_agent %} + + {% endif %} + + {% endfor %} + +
IDНазваниеГородДисциплинаДействие
{{ team.id }}{{ team.name.name }} {{ team.city }}{{ team.discipline_name }} + {% block action_button %} + {# Здесь в шаблонах-потомках должна быть кнопка#} + {# для удаления или добавления команды в соревнование.#} + {% endblock %} +
diff --git a/adaptive_hockey_federation/templates/main/competitions_id/components/competitions_teams_table_available.html b/adaptive_hockey_federation/templates/main/competitions_id/components/competitions_teams_table_available.html new file mode 100644 index 00000000..f6f3dfe5 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/competitions_id/components/competitions_teams_table_available.html @@ -0,0 +1,17 @@ +{% extends "main/competitions_id/components/competition_teams_table_base.html" %} +{% block action_button %} +
+ {% csrf_token %} + +
+{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/fields/check_box_field.html b/adaptive_hockey_federation/templates/main/fields/check_box_field.html new file mode 100644 index 00000000..04d6e131 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/check_box_field.html @@ -0,0 +1,7 @@ +{% load user_filters %} + + {{ field.label_tag }} + + {{ field |addclass:"base-checkbox"}} + + diff --git a/adaptive_hockey_federation/templates/main/fields/city_field.html b/adaptive_hockey_federation/templates/main/fields/city_field.html new file mode 100644 index 00000000..888f8c11 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/city_field.html @@ -0,0 +1,17 @@ +{% load user_filters %} + + {{ field.label_tag }} + + {% if field.errors %} + {{ field |addclass:'base-input-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-input'}} + + {% for city in cities %} + + {% endfor %} + + {% endif %} + + diff --git a/adaptive_hockey_federation/templates/main/fields/date_field.html b/adaptive_hockey_federation/templates/main/fields/date_field.html new file mode 100644 index 00000000..bc778042 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/date_field.html @@ -0,0 +1,11 @@ + + {{ field.label_tag }} + + {% if field.errors %} + {{ field |addclass:'base-date-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-date'}} + {% endif %} + + diff --git a/adaptive_hockey_federation/templates/main/fields/diagnosis_field.html b/adaptive_hockey_federation/templates/main/fields/diagnosis_field.html new file mode 100644 index 00000000..aa8b1af2 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/diagnosis_field.html @@ -0,0 +1,17 @@ +{% load user_filters %} + + {{ field.label_tag }} + + {% if field.errors %} + {{ field |addclass:'base-input-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-input'}} + + {% for diagnos in diagnosis %} + + {% endfor %} + + {% endif %} + + diff --git a/adaptive_hockey_federation/templates/main/fields/discipline_select_field.html b/adaptive_hockey_federation/templates/main/fields/discipline_select_field.html new file mode 100644 index 00000000..cafb1696 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/discipline_select_field.html @@ -0,0 +1,38 @@ +{% load user_filters %} + + {{ field.label_tag }} + +
+
+

Доступные дисциплины + +

+

+ + +

+ {% if form.available_disciplines.errors %} + {{ form.available_disciplines|addclass:"base-select-teams-errors" }} + {{ form.available_disciplines.errors}} + {% else %} + {{ form.available_disciplines|addclass:"base-select-teams" }} + {% endif %} +
+
+

Выбранные дисциплины + +

+

+ + +

+ {% if field.errors %} + {{ field|addclass:"base-select-teams-errors"}} + {{ field.errors}} + {% else %} + {{ field|addclass:"base-select-teams"}} + {% endif %} +
+
+ + diff --git a/adaptive_hockey_federation/templates/main/fields/documents_field.html b/adaptive_hockey_federation/templates/main/fields/documents_field.html new file mode 100644 index 00000000..ec1c9a1b --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/documents_field.html @@ -0,0 +1,5 @@ + + {{ doc.name }} + Просмотреть + + diff --git a/adaptive_hockey_federation/templates/main/fields/select_field.html b/adaptive_hockey_federation/templates/main/fields/select_field.html new file mode 100644 index 00000000..13d93017 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/select_field.html @@ -0,0 +1,15 @@ +{% load user_filters %} +{% load static %} + + {{ field.label_tag }} + + {% if field.errors %} + {{ field |addclass:'base-select-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-select'}} + {% endif %} + + + + diff --git a/adaptive_hockey_federation/templates/main/fields/team_select_field_1_field.html b/adaptive_hockey_federation/templates/main/fields/team_select_field_1_field.html new file mode 100644 index 00000000..69881eb6 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/team_select_field_1_field.html @@ -0,0 +1,26 @@ +{% load user_filters %} + + {{ field.label_tag }} + + {% if field.errors %} +
+
+

Доступные команды + +

+ {{ field|addclass:"base-select-teams-errors"}} + {{ field.errors}} +
+
+ {% else %} +
+
+

Доступные команды + +

+ {{ field|addclass:"base-select-teams"}} +
+
+ {% endif %} + + diff --git a/adaptive_hockey_federation/templates/main/fields/team_select_field_2_fields.html b/adaptive_hockey_federation/templates/main/fields/team_select_field_2_fields.html new file mode 100644 index 00000000..23c07856 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/team_select_field_2_fields.html @@ -0,0 +1,38 @@ +{% load user_filters %} + + {{ field.label_tag }} + +
+
+

Доступные команды + +

+

+ + +

+ {% if form.available_teams.errors %} + {{ form.available_teams |addclass:"base-select-teams-errors"}} + {{ form.available_teams.errors}} + {% else %} + {{ form.available_teams |addclass:"base-select-teams"}} + {% endif %} +
+
+

{{ help_text_role }} + +

+

+ + +

+ {% if field.errors %} + {{ field |addclass:"base-select-teams-errors"}} + {{ field.errors}} + {% else %} + {{ field |addclass:"base-select-teams"}} + {% endif %} +
+
+ + diff --git a/adaptive_hockey_federation/templates/main/fields/text_input_field.html b/adaptive_hockey_federation/templates/main/fields/text_input_field.html new file mode 100644 index 00000000..16220f66 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/fields/text_input_field.html @@ -0,0 +1,16 @@ +{% load user_filters %} + + {{ field.label_tag }} + {% if field.name in 'birthday identity_document phone email' %} + + {% endif %} + + + {% if field.errors %} + {{ field |addclass:'base-input-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-input'}} + {% endif %} + + diff --git a/adaptive_hockey_federation/templates/main/games/base_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/games/base_edit_delete_buttons.html new file mode 100644 index 00000000..136653bb --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/base_edit_delete_buttons.html @@ -0,0 +1,19 @@ +{% load static %} + +
+
+ +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/games/game_button_create.html b/adaptive_hockey_federation/templates/main/games/game_button_create.html new file mode 100644 index 00000000..9ffdc34f --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/game_button_create.html @@ -0,0 +1,2 @@ +{% url "games:game_create" as game_create_url %} +{% include 'base/button_create.html' with button_url=game_create_url name="Создать" current_url_name=request.path %} diff --git a/adaptive_hockey_federation/templates/main/games/game_create_edit.html b/adaptive_hockey_federation/templates/main/games/game_create_edit.html new file mode 100644 index 00000000..08ed5890 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/game_create_edit.html @@ -0,0 +1,65 @@ +{% extends "base/base.html" %} +{% load user_filters %} +{% block title %} + {{ page_title }} +{% endblock title %} + +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

{{ object.name }}

+ + + {% for field in form.visible_fields %} + {% if field.name in 'name video_link' %} + {% include "main/fields/text_input_field.html" %} + {% elif field.name in 'game_teams' %} + {% include "main/fields/team_select_field_2_fields.html" %} + {% elif field.name in 'competition' %} + {% include "main/fields/select_field.html" %} + {% elif field.name in 'date' %} + + + + + {% endif %} + {% endfor %} + +
{{ field.label_tag }} + {% if field.errors %} + {{ field |addclass:'base-date-errors'}} + {{ field.errors }} + {% else %} + {{ field |addclass:'base-date'}} + {% endif %} +
+
+ +
+
+
+ +
+
+
+
+
+ +{% endblock %} +{% block JavaScript %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/games/game_detail.html b/adaptive_hockey_federation/templates/main/games/game_detail.html new file mode 100644 index 00000000..4eb755bf --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/game_detail.html @@ -0,0 +1,90 @@ +{% extends "base/base.html" %} +{% load user_filters %} + +{% block title %} +{{ page_title }} +{% endblock title %} + +{% block content %} +{% include 'base/messages.html' %} +

Детали игры

+ +
+

Название: {{ object.name }}

+
Дата: {{ object.date }}
+
+ +

Ссылка на видео: {{ object.video_link }}

+

Ссылка на соревнование: {{ object.competition + }}

+ +
+
+
+ {% if teams|length > 0 %} +

{{ teams.0.name }}

+ + + + + + + + + {% for player in teams.0.players %} + + + + + {% empty %} + + + + {% endfor %} + +
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
+ + Редактировать номера игроков + + {% else %} +

К сожалению, информация о первой команде недоступна.

+ {% endif %} +
+ +
+ {% if teams|length > 1 %} +

{{ teams.1.name }}

+ + + + + + + + + {% for player in teams.1.players %} + + + + + {% empty %} + + + + {% endfor %} + +
Имя игрокаНомер игрока
{{ player.last_name }} {{ player.name }}{{ player.number }}
В этой команде нет игроков.
+ + Редактировать номера игроков + + {% else %} +

К сожалению, информация о второй команде недоступна.

+ {% endif %} +
+
+
+{% endblock content %} diff --git a/adaptive_hockey_federation/templates/main/games/game_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/games/game_edit_delete_buttons.html new file mode 100644 index 00000000..0a4c2da5 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/game_edit_delete_buttons.html @@ -0,0 +1 @@ +{% include "main/games/base_edit_delete_buttons.html" with action_url="games:game_edit" delete_url="games:delete_game" delete_message='Вы точно хотите удалить соревнование ' %} diff --git a/adaptive_hockey_federation/templates/main/games/games.html b/adaptive_hockey_federation/templates/main/games/games.html new file mode 100644 index 00000000..14a11584 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/games.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Игры +{% endblock %} +{% block content %} + {% include 'main/games/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/games/player_number_edit.html b/adaptive_hockey_federation/templates/main/games/player_number_edit.html new file mode 100644 index 00000000..5a7b3c00 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/player_number_edit.html @@ -0,0 +1,58 @@ +{% extends "base/base.html" %} +{% load user_filters %} +{% block title %} + {{ page_title }} +{% endblock title %} + +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

Команда: {{ game_team.name }}

+ + + {{ form.formset.management_form }} + {% for form in form.formset.forms %} + + + + + {% endfor %} + +
{{ form.instance.last_name }} {{ form.instance.name }} + {{ form.id }} + {% if form.errors %} + {{ form.number|addclass:'base-input-errors' }} + {{ form.gameplayer_id|addclass:'base-input-errors' }} + {% for error in form.errors.values %} + {{ error }} + {% endfor %} + {% else %} + {{ form.number|addclass:'base-input' }} + {{ form.gameplayer_id|addclass:'base-input' }} + {% endif %} +
+
+
+
+
+ +
+
+
+
+
+ {% if form.formset.non_form_errors %} +
+ {{ form.formset.non_form_errors }} +
+ {% endif %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/games/table.html b/adaptive_hockey_federation/templates/main/games/table.html new file mode 100644 index 00000000..a35a9114 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/games/table.html @@ -0,0 +1,54 @@ +{% load static %} + + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if view_name == "games:games" %} + {% include "main/games/game_edit_delete_buttons.html" %} + {% else %} + + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {% if cell_name == "video_link" %} + {{ cell_value }} + {% else %} + {% if cell_name == "name" %} + {{ cell_value }} + {% else %} + {% if cell_name == '__ref__'%} + + {{ cell_value.name }} + + {% else %} + {{ cell_value }} + {%endif%} + {% endif %} + {% endif %} + +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
diff --git a/adaptive_hockey_federation/templates/main/home/main.html b/adaptive_hockey_federation/templates/main/home/main.html new file mode 100644 index 00000000..f6104665 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/home/main.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Главная страница +{% endblock %} +{% block content %} + {% include 'main/home/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/home/search_form.html b/adaptive_hockey_federation/templates/main/home/search_form.html new file mode 100644 index 00000000..b1c46d6d --- /dev/null +++ b/adaptive_hockey_federation/templates/main/home/search_form.html @@ -0,0 +1,11 @@ +
+
+ + + +
+
diff --git a/adaptive_hockey_federation/templates/main/home/table.html b/adaptive_hockey_federation/templates/main/home/table.html new file mode 100644 index 00000000..f223f5a8 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/home/table.html @@ -0,0 +1,41 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk'%} + + {% endif %} + {% endfor %} + + {% endfor %} + +
{{ head_item }}
+ {% if 'main.view_player' in perms %} + {{ cell_value }} + {% else %} + {{ cell_value }} + {% endif %} + + {% include 'main/players/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + {{ cell_value }}
+
diff --git a/adaptive_hockey_federation/templates/main/player_id/MOCK_player_id_video_response.html b/adaptive_hockey_federation/templates/main/player_id/MOCK_player_id_video_response.html new file mode 100644 index 00000000..7908c2b8 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/MOCK_player_id_video_response.html @@ -0,0 +1,10 @@ +{% extends 'base/base.html' %} +{% load static %} + +{% block title %} + Запрос видео +{% endblock %} + +{% block content %} +

Видео в обработке

+{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/player_id/edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/player_id/edit_delete_buttons.html new file mode 100644 index 00000000..0b57397c --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/edit_delete_buttons.html @@ -0,0 +1,11 @@ +
+
+ Редактировать + {% if not user.is_moderator %} +
+ {% csrf_token %} + +
+ {% endif %} +
+
diff --git a/adaptive_hockey_federation/templates/main/player_id/player_id.html b/adaptive_hockey_federation/templates/main/player_id/player_id.html new file mode 100644 index 00000000..a18bd6b8 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/player_id.html @@ -0,0 +1,77 @@ +{% extends 'base/base.html' %} +{% load static %} +{% block title %} + Профиль игрока +{% endblock %} +{% block content %} +

Профиль игрока

+
+
+
+

Личная информация

+ + + {% for field_name, field_value in player_fields_personal %} + + + + + {% endfor %} + +
{{ field_name }}: + {{ field_value }} +
+
+ +
+

Игровая информация

+ + + {% for field_name, field_value in player_fields %} + + + + + {% endfor %} + +
{{ field_name }}: + {% if field_name == 'Капитан' or field_name == 'Ассистент' %} + {% if field_value %} + Да + {% endif %} + {% elif field_name == 'Команда' %} + {% for team in field_value %} + {{ team.name }} + {% if not forloop.last %}
{% endif %} + {% endfor %} + {% else %} + {{ field_value }} + {% endif %} +
+
+ {% include 'main/player_id/video_games_button.html' with player=player %} +
+
+ +
+

Документы

+ + + {% for doc in player_documents %} + + + + + {% endfor %} + +
{{ doc.name }} + Просмотреть +
+
+
+
+ +
+ {% include 'main/player_id/edit_delete_buttons.html' %} +
+{% endblock content %} diff --git a/adaptive_hockey_federation/templates/main/player_id/player_id_create_edit.html b/adaptive_hockey_federation/templates/main/player_id/player_id_create_edit.html new file mode 100644 index 00000000..2afc397d --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/player_id_create_edit.html @@ -0,0 +1,135 @@ +{% extends 'base/base.html' %} +{% load static %} +{% load user_filters %} +{% block title %} + {{ page_title }} +{% endblock %} + +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

Личная информация

+ + + {% for field in form.visible_fields %} + {% if field.name in 'surname name patronymic birthday identity_document '%} {% include "main/fields/text_input_field.html" %} + {% elif field.name in 'gender discipline_name discipline_level nosology' %} {% include "main/fields/select_field.html" %} + {% elif field.name in 'diagnosis' %} {% include "main/fields/diagnosis_field.html" %} + {% endif %} + {% endfor %} + +
+
+
+

Игровая информация

+ + + {% for field in form.visible_fields %} + {% if field.name in "level_revision number"%} {% include "main/fields/text_input_field.html" %} + {% elif field.name in "team" %} {% include "main/fields/team_select_field_2_fields.html" %} + {% elif field.name in "is_captain is_assistent" %} {% include "main/fields/check_box_field.html" %} + {% elif field.name in "position" %} {% include "main/fields/select_field.html" %} + {% endif %} + {% endfor %} + +
+
+

Документы

+ {% if errormessage %} +
{{ errormessage }}
+ {% endif %} + + + + + + + + + + {% if player_documents %} + {% for doc in player_documents %} {% include "main/fields/documents_field.html" %}{% endfor %} + {% endif %} + +
НазваниеФайлУдалить
+ + {% if team_id %} + + {% endif %} + +
+
+
+ +
+
+
+
+
+{% endblock %} +{% block JavaScript %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/player_id/player_id_deleted.html b/adaptive_hockey_federation/templates/main/player_id/player_id_deleted.html new file mode 100644 index 00000000..109c642c --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/player_id_deleted.html @@ -0,0 +1,11 @@ +{% extends 'base/base.html' %} +{% load static %} + +{% block title %} + Карточка удалена +{% endblock %} + +{% block content %} +

Карточка игрока успешно удалена

+

Вернуться к списку игроков

+{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/player_id/player_id_video_games.html b/adaptive_hockey_federation/templates/main/player_id/player_id_video_games.html new file mode 100644 index 00000000..0b60fde0 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/player_id_video_games.html @@ -0,0 +1,8 @@ +{% extends 'base/base.html' %} +{% block title %} + Видео игр +{% endblock %} +{% block content %} + {% include 'base/messages.html' %} + {% include 'main/games/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/player_id/video_games_button.html b/adaptive_hockey_federation/templates/main/player_id/video_games_button.html new file mode 100644 index 00000000..95d4166a --- /dev/null +++ b/adaptive_hockey_federation/templates/main/player_id/video_games_button.html @@ -0,0 +1,9 @@ +
+ {% if has_video_games %} + + {% endif %} +
diff --git a/adaptive_hockey_federation/templates/main/players/cell_button.html b/adaptive_hockey_federation/templates/main/players/cell_button.html new file mode 100644 index 00000000..7126b4ec --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/cell_button.html @@ -0,0 +1,4 @@ +{{ name }} + diff --git a/adaptive_hockey_federation/templates/main/players/create_delete_button_players.html b/adaptive_hockey_federation/templates/main/players/create_delete_button_players.html new file mode 100644 index 00000000..ac2e4f1a --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/create_delete_button_players.html @@ -0,0 +1,16 @@ +{% load static %} + +
+ + + +
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/players/create_delete_buttons.html b/adaptive_hockey_federation/templates/main/players/create_delete_buttons.html new file mode 100644 index 00000000..a023dddf --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/create_delete_buttons.html @@ -0,0 +1,17 @@ +{% load static %} + +
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/players/player_button_create.html b/adaptive_hockey_federation/templates/main/players/player_button_create.html new file mode 100644 index 00000000..8a0f6b4d --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/player_button_create.html @@ -0,0 +1,4 @@ +{% if 'main.add_player' in perms %} + {% url 'main:player_create' as player_create_url %} + {% include 'base/button_create.html' with button_url=player_create_url name="Создать" current_url_name=request.path %} +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/players/players.html b/adaptive_hockey_federation/templates/main/players/players.html new file mode 100644 index 00000000..071ae41c --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/players.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Список игроков +{% endblock %} +{% block content %} + {% include 'main/players/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/players/table.html b/adaptive_hockey_federation/templates/main/players/table.html new file mode 100644 index 00000000..fb0cce33 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/players/table.html @@ -0,0 +1,68 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk'%} + + {% endif %} + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if view_name == 'main:players' %} + {% include "main/players/create_delete_button_players.html" %} + {% else %} + + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {% if 'main.view_player' in perms %} + {{ cell_value }} + {% else %} + {{ cell_value }} + {% endif %} + + {% include 'main/players/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + + {% if cell_name == 'team' %} + {% for team in cell_value %} + {{ team }} +
+ {% endfor %} + {% else %} + {{ cell_value }} + {% endif %} +
+
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+
diff --git a/adaptive_hockey_federation/templates/main/staffs/add_button.html b/adaptive_hockey_federation/templates/main/staffs/add_button.html new file mode 100644 index 00000000..4124dc72 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/add_button.html @@ -0,0 +1,9 @@ +
+

Нет информации по команде

+ {% if perms.users.change_staffteammember %} +

Добавить сотрудника {% if position == "coach" %} тренером {% else %} пушер-тьютором {% endif %}

+ + {% endif %} +
diff --git a/adaptive_hockey_federation/templates/main/staffs/edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/staffs/edit_delete_buttons.html new file mode 100644 index 00000000..52299282 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/edit_delete_buttons.html @@ -0,0 +1,18 @@ +{% if perms.users.change_staffteammember %} +
+
+ Редактировать +
+ {% csrf_token %} + +
+
+
+{% endif %} diff --git a/adaptive_hockey_federation/templates/main/staffs/edit_delete_team_buttons.html b/adaptive_hockey_federation/templates/main/staffs/edit_delete_team_buttons.html new file mode 100644 index 00000000..44c2c673 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/edit_delete_team_buttons.html @@ -0,0 +1,28 @@ +{% if perms.users.change_staffteammember %} +
+
+ Редактировать + {% if staff.staff_position == 'тренер' %} +
+ {% else %} + + {% endif %} + {% csrf_token %} + +
+
+
+{% endif %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staff_button_create.html b/adaptive_hockey_federation/templates/main/staffs/staff_button_create.html new file mode 100644 index 00000000..9f51de44 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staff_button_create.html @@ -0,0 +1,2 @@ +{% url 'main:staff_create' as staff_create_url %} +{% include 'base/button_create.html' with button_url=staff_create_url name="Создать сотрудника" current_url_name=request.path %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staff_id.html b/adaptive_hockey_federation/templates/main/staffs/staff_id.html new file mode 100644 index 00000000..dc29bac8 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staff_id.html @@ -0,0 +1,70 @@ +{% extends 'base/base.html' %} +{% load static %} +{% block title %} + Профиль сотрудника команды +{% endblock %} +{% block content %} +

Профиль сотрудника команды

+
+

Личная информация

+ + + {% for field_name, field_value in staff_fields %} + + + + + {% endfor %} + +
{{ field_name }}: + {{ field_value }} +
+ {% include 'main/staffs/edit_delete_buttons.html' with staff=staff %} +
+

Информация о состоянии в команде

+
+ {% if team_fields_coach %} +
+

В должности тренера:

+ + + {% for field_name, field_value in team_fields_coach %} + + + + + {% endfor %} + +
{{ field_name }}: + {{ field_value }} +
+ {% include 'main/staffs/edit_delete_team_buttons.html' with staff=coach %} +
+ {% else%} +
+ {% include 'main/staffs/add_button.html' with position="coach" %} +
+ {% endif %} + {% if team_fields_pusher %} +
+

В должности пушер-тьютора:

+ + + {% for field_name, field_value in team_fields_pusher %} + + + + + {% endfor %} + +
{{ field_name }}: + {{ field_value }} +
+ {% include 'main/staffs/edit_delete_team_buttons.html' with staff=pusher %} +
+ {% else %} + {% include 'main/staffs/add_button.html' with position="pusher" %} + {% endif %} +
+
+{% endblock content %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staff_id_create_edit.html b/adaptive_hockey_federation/templates/main/staffs/staff_id_create_edit.html new file mode 100644 index 00000000..324acf2b --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staff_id_create_edit.html @@ -0,0 +1,38 @@ +{% extends 'base/base.html' %} +{% load static %} +{% load user_filters %} +{% block title %} + Редактирование сотрудника команды +{% endblock %} +{% block content %} +

{{ page_title }}

+ +
+ {% csrf_token %} +
+
+

Информация о сотруднике

+ + + {% for field in form.visible_fields %} + {% if field.name in 'surname name patronymic phone' %} {% include "main/fields/text_input_field.html" %} {% endif %} + {% endfor %} + +
+
+ +
+
+
+ +
+
+
+ +
+{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staff_id_team_edit_create.html b/adaptive_hockey_federation/templates/main/staffs/staff_id_team_edit_create.html new file mode 100644 index 00000000..0cb5b29f --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staff_id_team_edit_create.html @@ -0,0 +1,50 @@ +{% extends 'base/base.html' %} +{% load static %} +{% load user_filters %} +{% block title %} + Редактирование сотрудника команды +{% endblock %} +{% block content %} +

{{ page_title }}

+ +
+ {% csrf_token %} +
+ + + {% for field in form.visible_fields %} + {% if field.name in "qualification notes"%} {% include "main/fields/text_input_field.html" %} + {% elif field.name in "team" %} {% include "main/fields/team_select_field_2_fields.html" %} + {% endif %} + {% endfor %} + +
+
+ +
+
+
+ +
+
+
+
+
+{% endblock %} + + +{% block JavaScript %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staffs.html b/adaptive_hockey_federation/templates/main/staffs/staffs.html new file mode 100644 index 00000000..9cf71e5e --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staffs.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Персонал команд +{% endblock %} +{% block content %} + {% include 'main/staffs/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/staffs/staffs_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/staffs/staffs_edit_delete_buttons.html new file mode 100644 index 00000000..911db369 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/staffs_edit_delete_buttons.html @@ -0,0 +1,16 @@ +{% load static %} + +
+ + + +
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/staffs/table.html b/adaptive_hockey_federation/templates/main/staffs/table.html new file mode 100644 index 00000000..cbe77915 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/staffs/table.html @@ -0,0 +1,59 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk'%} + + {% endif %} + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if view_name == 'main:staffs' %} + {% include "main/staffs/staffs_edit_delete_buttons.html" %} + {% else %} + + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {% if 'main.view_staff' in perms %} + {{ cell_value }} + {% else %} + {{ cell_value }} + {% endif %} + + {% include 'base/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + {{ cell_value }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+
diff --git a/adaptive_hockey_federation/templates/main/teams/base_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/teams/base_edit_delete_buttons.html new file mode 100644 index 00000000..b7edd34d --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/base_edit_delete_buttons.html @@ -0,0 +1,24 @@ +{% load static %} + +
+
+ {% if perms.main.change_team %} + + {% endif %} +
+
+ {% csrf_token %} + {% if perms.main.delete_team %} + + {% endif %} +
+
+ diff --git a/adaptive_hockey_federation/templates/main/teams/cell_button.html b/adaptive_hockey_federation/templates/main/teams/cell_button.html new file mode 100644 index 00000000..7126b4ec --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/cell_button.html @@ -0,0 +1,4 @@ +{{ name }} + diff --git a/adaptive_hockey_federation/templates/main/teams/create_delete_buttons.html b/adaptive_hockey_federation/templates/main/teams/create_delete_buttons.html new file mode 100644 index 00000000..004778a0 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/create_delete_buttons.html @@ -0,0 +1,24 @@ +{% load static %} + +
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/teams/filters.html b/adaptive_hockey_federation/templates/main/teams/filters.html new file mode 100644 index 00000000..43672011 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/filters.html @@ -0,0 +1,11 @@ +
+
+ {% for field in form %} +
+ {{ field.label }} + {{ field }} +
+ {% endfor %} +
+ +
diff --git a/adaptive_hockey_federation/templates/main/teams/table.html b/adaptive_hockey_federation/templates/main/teams/table.html new file mode 100644 index 00000000..1b356a33 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/table.html @@ -0,0 +1,47 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk' and cell_name != 'allow_edit' %} + + {% endif %} + + {% if cell_name == 'allow_edit' and cell_value == True %} + {% include "main/teams/team_edit_delete_buttons.html" %} + {% endif %} + {% endfor %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {{ cell_value }} + + {% include 'base/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + {{ cell_value }}
+
diff --git a/adaptive_hockey_federation/templates/main/teams/team_create_edit.html b/adaptive_hockey_federation/templates/main/teams/team_create_edit.html new file mode 100644 index 00000000..977d931d --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/team_create_edit.html @@ -0,0 +1,51 @@ +{% extends 'base/base.html' %} +{% load user_filters %} +{% block title %} + Редактирование данных команды +{% endblock %} +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

{{ object.name }}

+ + + {% for field in form.visible_fields %} + {% if field.name in 'name'%} {% include "main/fields/text_input_field.html" %} + {% elif field.name in 'city'%} {% include "main/fields/city_field.html" %} + {% elif field.name in 'discipline_name curator' %} {% include "main/fields/select_field.html" %} + {% endif %} + {% endfor %} + +
+
+ +
+
+
+ +
+
+
+
+
+{% endblock %} + +{% block JavaScript %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/teams/team_edit_delete_buttons.html b/adaptive_hockey_federation/templates/main/teams/team_edit_delete_buttons.html new file mode 100644 index 00000000..36231150 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/team_edit_delete_buttons.html @@ -0,0 +1 @@ +{% include "main/teams/base_edit_delete_buttons.html" with action_url="main:team_update" delete_url="main:team_delete" is_activated=perms.team.change_team delete_message='Вы точно хотите удалить команду ' %} diff --git a/adaptive_hockey_federation/templates/main/teams/team_form_city_field_datalist.html b/adaptive_hockey_federation/templates/main/teams/team_form_city_field_datalist.html new file mode 100644 index 00000000..40d7d648 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/team_form_city_field_datalist.html @@ -0,0 +1,7 @@ +{% if field.name == 'city' %} + + {% for city in cities %} + + {% endfor %} + +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/teams/teams.html b/adaptive_hockey_federation/templates/main/teams/teams.html new file mode 100644 index 00000000..b4cc2be5 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams/teams.html @@ -0,0 +1,12 @@ +{% extends 'base/base.html' %} +{% block title %} + Список команд +{% endblock %} +{% block content_navigation %} +
+ {% include 'main/teams/filters.html' with form=form_filter %} +
+{% endblock %} +{% block content %} + {% include 'main/teams/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/teams_id/button_create_team_member.html b/adaptive_hockey_federation/templates/main/teams_id/button_create_team_member.html new file mode 100644 index 00000000..9a6c8eaa --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/button_create_team_member.html @@ -0,0 +1,9 @@ + diff --git a/adaptive_hockey_federation/templates/main/teams_id/cell_button.html b/adaptive_hockey_federation/templates/main/teams_id/cell_button.html new file mode 100644 index 00000000..7126b4ec --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/cell_button.html @@ -0,0 +1,4 @@ +{{ name }} + diff --git a/adaptive_hockey_federation/templates/main/teams_id/components/staff_search_field.html b/adaptive_hockey_federation/templates/main/teams_id/components/staff_search_field.html new file mode 100644 index 00000000..1e462469 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/components/staff_search_field.html @@ -0,0 +1,7 @@ +{% if field.name == 'staffteammember' %} + + {% for staff_member in staff.data_list %} + + {% endfor %} + +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/teams_id/create_delete_buttons.html b/adaptive_hockey_federation/templates/main/teams_id/create_delete_buttons.html new file mode 100644 index 00000000..bf93a014 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/create_delete_buttons.html @@ -0,0 +1,19 @@ +{% load static %} +{%if user.is_staff %} + +
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
+
+ +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_players.html b/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_players.html new file mode 100644 index 00000000..721ac571 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_players.html @@ -0,0 +1,18 @@ +{% load static %} + +
+ + + + {% if user.is_staff %} +
+ {% csrf_token %} + +
+ {% endif %} +
+ diff --git a/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_staffs.html b/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_staffs.html new file mode 100644 index 00000000..3cfd8994 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/edit_delete_button_staffs.html @@ -0,0 +1,24 @@ +{% load static %} +{% if user.is_staff %} + +
+ + + +
+ {% csrf_token %} + +
+
+ +{% endif %} diff --git a/adaptive_hockey_federation/templates/main/teams_id/table.html b/adaptive_hockey_federation/templates/main/teams_id/table.html new file mode 100644 index 00000000..ff55e433 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/table.html @@ -0,0 +1,67 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + {% if table_name == "players" and request.user == team.curator or request.user.is_admin %} + + {% endif %} + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name == 'full_name_link' %} + + {% elif cell_name != 'url' and cell_name != 'id' and cell_name != 'pk' and cell_name != 'staff_position_slug'%} + + {% endif %} + {% endfor %} + {% if table_name == 'players' %} + {% if request.user.is_admin or request.user == team.curator %} + {% include "main/teams_id/edit_delete_button_players.html" %} + {% endif %} + {% elif table_name == 'staff' %} + {% include "main/teams_id/edit_delete_button_staffs.html" %} + {% else %} + + {% endif %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {{ cell_value }} + + {% include 'teams_id/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + + {% if request.user.is_admin or request.user == team.curator %} + {{ cell_value.name }} + {% else %} + {{ cell_value.name }} + {% endif %} + {{ cell_value }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+
diff --git a/adaptive_hockey_federation/templates/main/teams_id/teams_id.html b/adaptive_hockey_federation/templates/main/teams_id/teams_id.html new file mode 100644 index 00000000..507a0ad8 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/teams_id/teams_id.html @@ -0,0 +1,54 @@ +{% extends 'base/base.html' %} +{% load user_filters %} +{% block title %} + Профиль команды: {{ team }} +{% endblock %} +{% block content %} +

Команда: {{ team }}

+ {% for staff in staff_table %} +
+

{{ staff.position }}

+ {% include 'main/teams_id/table.html' with table_head=staff.head table_data=staff.data table_name='staff' %} + +
+ {% csrf_token %} + {% if request.user == team.curator or user.is_staff %} + {% for field in staff.add_staff_form %} +
+
+ + {{ field|addclass:'form-control' }} + {% include 'main/teams_id/components/staff_search_field.html' %} + {% if field.errors %} + {{ field.errors|striptags }} + {% endif %} +
+
+ {% endfor %} + {% endif %} + {% if request.user == team.curator or user.is_staff %} + + {% endif %} +
+
+ {% endfor %} +
+

{{ players_table.name }}

+ {% include 'main/teams_id/table.html' with table_head=players_table.head table_data=players_table.data table_name='players' %} +
+{% endblock content %} diff --git a/adaptive_hockey_federation/templates/main/unloads/base_delete_buttons.html b/adaptive_hockey_federation/templates/main/unloads/base_delete_buttons.html new file mode 100644 index 00000000..b4347d1c --- /dev/null +++ b/adaptive_hockey_federation/templates/main/unloads/base_delete_buttons.html @@ -0,0 +1,12 @@ +{% load static %} + +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/unloads/table.html b/adaptive_hockey_federation/templates/main/unloads/table.html new file mode 100644 index 00000000..5232ad4f --- /dev/null +++ b/adaptive_hockey_federation/templates/main/unloads/table.html @@ -0,0 +1,41 @@ +{% load static %} + + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if view_name == 'unloads:unloads' %} + {% include "main/unloads/unloads_delete.html" %} + {% else %} + + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_delete_icon.html' %} +
+
+ {% if cell_name == '_ref_' %} + {% include 'base/cell_button.html' with url=cell_value.url name=cell_value.name %} + {% else %} + {{ cell_value }} + {% endif %} + +
+ {% include 'base/active_delete_icon.html' %} +
+
diff --git a/adaptive_hockey_federation/templates/main/unloads/unloads.html b/adaptive_hockey_federation/templates/main/unloads/unloads.html new file mode 100644 index 00000000..f1d29937 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/unloads/unloads.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Выгрузки +{% endblock %} +{% block content %} + {% include 'main/unloads/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/unloads/unloads_delete.html b/adaptive_hockey_federation/templates/main/unloads/unloads_delete.html new file mode 100644 index 00000000..76418493 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/unloads/unloads_delete.html @@ -0,0 +1 @@ +{% include "main/unloads/base_delete_buttons.html" with delete_url="unloads:delete_unload" delete_message='Вы точно хотите удалить выгрузку ' %} diff --git a/adaptive_hockey_federation/templates/main/users/create_delete_buttons.html b/adaptive_hockey_federation/templates/main/users/create_delete_buttons.html new file mode 100644 index 00000000..6f75f786 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/users/create_delete_buttons.html @@ -0,0 +1,16 @@ +{% load static %} + +
+ +
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/main/users/list.html b/adaptive_hockey_federation/templates/main/users/list.html new file mode 100644 index 00000000..4deb7ee6 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/users/list.html @@ -0,0 +1,7 @@ +{% extends 'base/base.html' %} +{% block title %} + Список пользователей +{% endblock %} +{% block content %} + {% include 'main/users/table.html' with table_head=table_head table_data=table_data %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/main/users/table.html b/adaptive_hockey_federation/templates/main/users/table.html new file mode 100644 index 00000000..8bbbf72a --- /dev/null +++ b/adaptive_hockey_federation/templates/main/users/table.html @@ -0,0 +1,55 @@ +{% load static %} +
+ + + + {% for head_item in table_head.values %} + + {% endfor %} + + + + + {% for table_string in table_data %} + + {% for cell_name, cell_value in table_string.items %} + {% if forloop.counter0 == 0 and 'url' in table_string %} + + {% elif cell_name == '_ref_' %} + {% if cell_value.type == 'button' %} + + {% else %} + + {% endif %} + {% elif cell_name != 'url' and cell_name != 'id'%} + + {% endif %} + {% endfor %} + {% with request.resolver_match.view_name as view_name %} + {% if view_name == 'users:users' %} + {% include "main/users/create_delete_buttons.html" %} + {% else %} + + {% endif %} + {% endwith %} + + {% endfor %} + +
{{ head_item }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+ {{ cell_value }} + + {% include 'main/users/cell_button.html' with url=cell_value.url name=cell_value.name %} + + {{ cell_value.name }} + {{ cell_value }} +
+ {% include 'base/active_edit_icon.html' %} + {% include 'base/active_delete_icon.html' %} +
+
+
diff --git a/adaptive_hockey_federation/templates/main/users/user_create_edit.html b/adaptive_hockey_federation/templates/main/users/user_create_edit.html new file mode 100644 index 00000000..9c0951f5 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/users/user_create_edit.html @@ -0,0 +1,84 @@ +{% extends "base/base.html" %} +{% load static %} +{% load user_filters %} +{% block title %} + {{ page_title }} +{% endblock %} +{% block content %} +

{{ page_title }}

+
+ {% csrf_token %} +
+
+

Личная информация

+ + + {% for field in form.visible_fields %} + {% if field.name in 'first_name last_name patronymic email phone'%} {% include "main/fields/text_input_field.html" %} {% endif %} + {% endfor %} + +
+
+
+

Дополнительная информация

+ + + {% for field in form.visible_fields %} + {% if field.name in "role"%}{% include "main/fields/select_field.html" %} {% endif %} + {% if field.name in "team"%}{% include "main/fields/team_select_field_1_field.html" %} {% endif %} + {% endfor %} + +
+
+
+
+
+ +
+
+
+
+
+{% endblock %} + +{% block JavaScript %} + +{% endblock%} diff --git a/adaptive_hockey_federation/templates/main/users/users_buttons.html b/adaptive_hockey_federation/templates/main/users/users_buttons.html new file mode 100644 index 00000000..93474a32 --- /dev/null +++ b/adaptive_hockey_federation/templates/main/users/users_buttons.html @@ -0,0 +1,17 @@ +{% load static %} + +
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
+
+ diff --git a/adaptive_hockey_federation/templates/registration/base.html b/adaptive_hockey_federation/templates/registration/base.html new file mode 100644 index 00000000..908b7b1e --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/base.html @@ -0,0 +1,19 @@ + + + + {% load static %} + {% include 'registration/includes/head.html' %} + + + {% block title %} {% endblock %} + + + + {% include 'registration/includes/header.html'%} + {% block content %} + Контент не подвезли + {% endblock %} + {% include 'registration/includes/footer.html'%} + + + diff --git a/adaptive_hockey_federation/templates/registration/includes/close_divs.html b/adaptive_hockey_federation/templates/registration/includes/close_divs.html new file mode 100644 index 00000000..b0f2d2fa --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/close_divs.html @@ -0,0 +1,2 @@ +
+
diff --git a/adaptive_hockey_federation/templates/registration/includes/fields_form.html b/adaptive_hockey_federation/templates/registration/includes/fields_form.html new file mode 100644 index 00000000..849dfa6e --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/fields_form.html @@ -0,0 +1,8 @@ +{% load user_filters %} + +{{ field|addclass:'form-layout' }} diff --git a/adaptive_hockey_federation/templates/registration/includes/footer.html b/adaptive_hockey_federation/templates/registration/includes/footer.html new file mode 100644 index 00000000..30e7bafd --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/footer.html @@ -0,0 +1,10 @@ +{% load static %} + diff --git a/adaptive_hockey_federation/templates/registration/includes/forms_errors.html b/adaptive_hockey_federation/templates/registration/includes/forms_errors.html new file mode 100644 index 00000000..b73ac5f0 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/forms_errors.html @@ -0,0 +1,14 @@ +{% if form.errors %} +{% for field in form %} + {% for error in field.errors %} +
+ {{ error|escape }} +
+ {% endfor %} +{% endfor %} +{% for error in form.non_field_errors %} +
+ {{ error|escape }} +
+{% endfor %} +{% endif %} diff --git a/adaptive_hockey_federation/templates/registration/includes/head.html b/adaptive_hockey_federation/templates/registration/includes/head.html new file mode 100644 index 00000000..4e18abb3 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/head.html @@ -0,0 +1,5 @@ +{% load static %} + + + + diff --git a/adaptive_hockey_federation/templates/registration/includes/header.html b/adaptive_hockey_federation/templates/registration/includes/header.html new file mode 100644 index 00000000..e4ea35ee --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/header.html @@ -0,0 +1,9 @@ +{% load static %} + diff --git a/adaptive_hockey_federation/templates/registration/includes/open_divs.html b/adaptive_hockey_federation/templates/registration/includes/open_divs.html new file mode 100644 index 00000000..4618bb73 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/includes/open_divs.html @@ -0,0 +1,2 @@ +
+
diff --git a/adaptive_hockey_federation/templates/registration/login.html b/adaptive_hockey_federation/templates/registration/login.html new file mode 100644 index 00000000..3de51eab --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/login.html @@ -0,0 +1,34 @@ +{% extends "registration/base.html" %} +{% load static %} +{% load user_filters %} +{% block title %} Логин {% endblock %} +{% block content %} + +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_change_done.html b/adaptive_hockey_federation/templates/registration/password_change_done.html new file mode 100644 index 00000000..4412020b --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_change_done.html @@ -0,0 +1,12 @@ +{% extends "registration/base.html" %} +{% block title %} Пароль изменён {% endblock %} +{% block content %} +{% include 'registration/includes/open_divs.html' %} +
+ Пароль изменён +
+
+

Пароль изменён успешно

+
+{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_change_form.html b/adaptive_hockey_federation/templates/registration/password_change_form.html new file mode 100644 index 00000000..2a0747c2 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_change_form.html @@ -0,0 +1,23 @@ +{% extends "registration/base.html" %} +{% block title %} Изменение пароля {% endblock %} +{% block content %} +{% include 'registration/includes/open_divs.html' %} +
+ Изменить пароль +
+
+ {% include 'registration/includes/forms_errors.html' %} +
+ {% for field in form %} + {% include 'registration/includes/fields_form.html' %} + {% endfor %} + +
+
+{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_reset_complete.html b/adaptive_hockey_federation/templates/registration/password_reset_complete.html new file mode 100644 index 00000000..dfeee8e7 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_reset_complete.html @@ -0,0 +1,19 @@ +{% extends "registration/base.html" %} +{% block title %} Сброс пароля прошёл успешно {% endblock %} +{% block content %} +{% include 'registration/includes/open_divs.html' %} +
+ Восстановление пароля завершено +
+
+{% csrf_token %} +
+

Ваш пароль был изменён. +

+ +
+
+{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_reset_confirm.html b/adaptive_hockey_federation/templates/registration/password_reset_confirm.html new file mode 100644 index 00000000..58287eda --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_reset_confirm.html @@ -0,0 +1,33 @@ +{% extends "registration/base.html" %} +{% block title %} Новый пароль {% endblock %} +{% block content %} +{% if validlink %} +{% include 'registration/includes/open_divs.html' %} +
+ +
+ {% endif %} + > + {% csrf_token %} + + {% for field in form %} + {% include 'registration/includes/fields_form.html' %} + {% endfor %} + +
+
+{% include 'registration/includes/forms_errors.html' %} +{% else %} +
+ Ошибка +
+
+

Ссылка сброса пароля содержит ошибку или устарела.

+
+{% endif %} +{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_reset_done.html b/adaptive_hockey_federation/templates/registration/password_reset_done.html new file mode 100644 index 00000000..2f6c6132 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_reset_done.html @@ -0,0 +1,15 @@ +{% extends "registration/base.html" %} +{% block title %} Сброс пароля прошёл успешно {% endblock %} +{% block content %} +{% include 'registration/includes/open_divs.html' %} +
+ Отправлено письмо +
+
+

Проверьте свою почту, вам должно прийти письмо со ссылкой для восстановления пароля

+ + Вернуться + +
+{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/templates/registration/password_reset_email.html b/adaptive_hockey_federation/templates/registration/password_reset_email.html new file mode 100644 index 00000000..137f8a03 --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_reset_email.html @@ -0,0 +1,12 @@ +{% autoescape off %} + To initiate the password reset process for your {{ user.email }} Django Registration/Login App Account, + click the link below: + + {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} + + If clicking the link above doesn't work, please copy and paste the URL in a new browser + window instead. + + Sincerely, + The Developer +{% endautoescape %} diff --git a/adaptive_hockey_federation/templates/registration/password_reset_form.html b/adaptive_hockey_federation/templates/registration/password_reset_form.html new file mode 100644 index 00000000..4b5ddb7d --- /dev/null +++ b/adaptive_hockey_federation/templates/registration/password_reset_form.html @@ -0,0 +1,25 @@ +{% extends "registration/base.html" %} +{% block title %} Сброс пароля {% endblock %} +{% block content %} +{% include 'registration/includes/open_divs.html' %} +
+ E-майл, под которым вы регистрировались +
+
+ {% include 'registration/includes/forms_errors.html' %} +
+ {% csrf_token %} + + {% for field in form %} + {% include 'registration/includes/fields_form.html' %} + {% endfor %} + +
+
+{% include 'registration/includes/close_divs.html' %} +{% endblock %} diff --git a/adaptive_hockey_federation/tests/__init__.py b/adaptive_hockey_federation/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/tests/base.py b/adaptive_hockey_federation/tests/base.py new file mode 100644 index 00000000..b062f6aa --- /dev/null +++ b/adaptive_hockey_federation/tests/base.py @@ -0,0 +1,1034 @@ +import copy +from http import HTTPStatus +from typing import Any, Iterable + +from competitions.models import Competition +from core import constants +from core.constants import Role +from django.core.exceptions import ValidationError +from django.db import transaction +from django.db.models import Model +from django.shortcuts import get_object_or_404 +from django.test import Client, TestCase +from factory.django import DjangoModelFactory # type: ignore +from main.data_factories.factories import ( + CompetitionFactory, + DiagnosisFactory, + DocumentFactory, + PlayerFactory, + StaffTeamMemberFactory, + TeamFactory, +) +from main.models import ( + Diagnosis, + DisciplineName, + Document, + Player, + StaffTeamMember, + Team, +) +from tests.fixture_user import test_role_user +from tests.url_test import TEST_GROUP_NAME +from tests.utils import render_url +from users.factories import UserFactory +from users.models import ProxyGroup, User + +URL_MSG = ( + "{method} запрос по адресу: {msg_url} должен вернуть ответ со статусом " + "{status_code}. Тестировался запрос по адресу: {url}" +) +TEST_SETUP_ERROR = "Неверные настройки теста:" + + +class BaseTestClass(TestCase): + """ + Базовый класс для тестирования. + + При инициализации создает в тестовой БД по одному экземпляру каждой из + задействованных в приложении моделей. Пользователей создается два: + суперпользователь и обычный пользователь. + """ + + superuser: User + user: User + discipline_name: DisciplineName + team: Team + diagnosis: Diagnosis + player: Player + staff: StaffTeamMember + competition: Competition + document: Document + + @classmethod + def setUpClass(cls) -> None: + """Классовый метод для базовой настройки всех тестов класса.""" + super().setUpClass() + ProxyGroup.objects.create(name=TEST_GROUP_NAME).save() + constants.GROUPS_BY_ROLE[test_role_user] = TEST_GROUP_NAME + cls.superuser = User.objects.create_user( + password="super1234", + first_name="Супер", + last_name="Пользователь", + role=Role.SUPERUSER, + email="superuser@fake.fake", + is_staff=True, + is_superuser=True, + ) + cls.user = UserFactory.create(role=test_role_user) + cls.team = TeamFactory.create() + cls.competition = CompetitionFactory.create() + cls.diagnosis = DiagnosisFactory.create() + cls.player = PlayerFactory.create() + cls.staff = StaffTeamMemberFactory.create() + cls.document = DocumentFactory.create(player=cls.player) + + +class UrlTestMixin: + """ + Миксин для тестирования url-адресов. + + Предоставляет унифицированный метод для тестирования + url-адресов, с генерацией информативного сообщения + """ + + def get_default_client(self) -> Client: + """ + Возвращает обычный неавторизованный клиент. + + Переопределите для тестирования нужного вам клиента. + """ + return self.client + + def url_get_test( + self, + urls: Iterable | str, + method: str = "get", + status_code: int = HTTPStatus.OK, + subs: tuple[str, ...] | str = "1", + client: Client | None = None, + message: str | None = None, + ): + """ + Унифицированный метод для урл-тестов. + + - urls: урл-адрес либо список или кортеж урл-адресов, подлежащих + тестированию; + - method: метод запроса (обычно "get" или "post", по умолчанию - + "get"); + - status_code: ожидаемый ответ, по умолчанию - 200. + - subs: строка или кортеж строк подстановки вместо + динамических идентификаторов объектов типа в урл-путях. + Например, для обработки пути: + "/competitions//teams//add/" + если передать кортеж ("1", "2"), то он преобразует путь в + "/competitions/1/teams/2/add/"). + Если подстановок больше, чем элементов в кортеже "subs", то для + оставшихся подстановок возьмется последний элемент. Если в примере + выше передать строку "1" или кортеж ("1",), то урл преобразуется в + "/competitions/1/teams/1/add/". Если в урл нет динамических + идентификаторов, данный параметр не имеет значения. + - client: клиент для тестирования. + Если не определить этот параметр, то клиент будет получен из + метода get_default_client(). Если метод get_default_client() не + переопределен в классе, то тестирование будет проводиться с + простым неавторизованным клиентом. + - message: кастомное сообщение в случае непрохождения теста, + если генерируемое методом сообщение по каким-то причинам не + устраивает. + """ + client = client or self.get_default_client() + if isinstance(urls, str) or isinstance(urls, dict): + urls = (urls,) + for url in urls: + if isinstance(url, dict): + url = url["url"] + url_for_message = url + url = render_url(url, subs) + msg = message or URL_MSG.format( + method=method.upper(), + msg_url=url_for_message, + status_code=status_code, + url=url, + ) + with self.__getattribute__("subTest")(url=url): + response = client.__getattribute__(method.lower())(url) + self.__getattribute__("assertEqual")( + response.status_code, + status_code, + msg=msg, + ) + + +class ModelTestBaseClass(BaseTestClass): + """ + Класс для CRUD-тестирования. + + Инициализация класса задана предком (BaseTestClass), который создает + для каждого внутреннего тестового метода все необходимые фикстуры. + + 1. Определите в классе - наследнике поля model и model_schema. + + Поле model - ссылка на класс соответствующей модели. + + Поле model_schema - схема тестирования. Оно должно представлять собой + словарь с ключами: + + - "correct_create": словарь с парами "поле": "значение поля", + которые будут использоваться как заведомо правильные значения полей + для создания тестовых объектов. Минимально необходимый нобор полей + для корректной работы - все обязательные поля. Остальные - по + необходимости для нужд тестов. Для полей типа "ForeignKey", + если они являются обязательными, надо указать ссылку на сам + класс соответствующей модели, например "city": City. По умолчанию + тест будет создавать объект с внешним ключом, в который запишет + объект указанной модели, взятый методом first() (в приведенном + примере - City.objects.first(). Необходимо позаботиться, чтобы хотя + бы один такой объект существовал. Изменить это поведение можно, + переопределив метод fill_foreign_keys(); + + - "correct_update": аналогично ключу "correct_create". Данный ключ + должен также содержать все обязательные поля, которые необходимы для + создания нового объекта, поскольку запрос выполняется методом POST. + Данный ключ будет использоваться для изменения тестовых объектов; + + - "must_not_be_admitted": кортеж со словарями для тестирования + валидиации отдельных полей на предмет недопуска в БД невалидных + значений. + Словарь для тестирования валидации отдельных полей должен + содержать два ключа: + - "fields", значением которого может быть строка с названием + тестируемого поля или кортеж из названий нескольких полей; + - "test_values", значением которого является кортеж из + одного или нескольких кортежей вида + ( + "тестовое НЕВАЛИДНОЕ значение", + "описание тестового значения" + ). + + Пример формирования ключа "must_not_be_admitted": ( + {"fields": ("name", "surname"), + "test_values": ( + ("1234567890", "значение из цифр"), + ("Пётр1", "цифры наряду с буквами"), + ), + {"fields": "patronymic", "test_values" ...какие-нибудь + тесты для поля patronymic... + ) + Обнаружив такой ключ, метод incorrect_field_tests + поочередно для каждого поля проведет тесты на валидацию + с указанными значениями, и вызовет AssertionError в том случае, + если поле с таким значением не вызывает ошибки валидации. + + Для элемента test_values можно вместо одного значения указать + также кортеж значений, тогда для каждого из указанных полей + будут протестированы каждый иэ элементов возможных некорректных + значений. Например, если в вышеуказанном примере вместо + ("123456790", "значение из цифр") указать + (tuple("1234567890"), "значение из цифр"), тест + поочередно попытается присвоить полю значения "1", "2", "3" и т.д., + и при первом непройденном тесте остановит его и перейдет к + тестированию значения "Пётр1". В этом же примере, опционально + справа и (или) слева от кортежа можно добавить произвольные + строковые значения, которые будут использоваться как префикс и + постфикс в тесте. Например: + ("Пётр ", tuple("1234567890"), " царь", "цифры наряду с буквами") + будут тестировать валидацию значений: + - "Пётр 1 царь"; + - "Пётр 2 царь"; + - "Пётр 3 царь" и т.д. + Такие префикс и постфикс могут быть строго строковыми + значениями, любой другой тип данных для них вызовет ошибку и(или) + неправильную отработку тестов. + + - "must_be_admitted": то же самое, что и "must_not_be_admitted", + но наоборот - здесь необходимо в качестве test_values + указывать те значения, которые ДОЛЖНЫ являться валидными и + сохраняться в БД штатно. Тест методом correct_field_tests() + попытается изменить объект и выдаст ошибку, если объект с измененным + значением не будет обнаружен. + + 2. В классе предусмотрены следующие методы для + тестирования: + - correct_create_tests() - тест на создание объекта; + - correct_update_tests() - тест на изменение объекта; + - correct_delete_tests() - тест на удаление объекта; + - incorrect_field_tests() - тест на защиту от невалидных данных + отдельных полей; + - incorrect_field_tests_via_url() - то же самое, но через url + - correct_field_tests() - тест на допуск валидных значений в + отдельные поля; + + !!! Вызов двух разных методов в одном тесте не рекомендуется, поскольку + между ними в таком случае не будет производиться сброс тестовой БД к + исходному состоянию, и результаты таких тестов могут быть некорректными !!! + + !!! Класс не предназначен для тестирования пермишенов, поэтому при + тестировании CRUD через url по умолчанию все запросы выполняются от + имени суперпользователя. Переопределить это поведение можно в методе + _post(), хотя делать это не рекомендуется. !!! + + """ + + model: type[Model] | None = None + model_schema: dict | None = None + model_factory: DjangoModelFactory | None = None + + def get_model(self): + """ + Возвращает класс тестовой модели. + + Переопределите этот метод, если нужна другая логика. + """ + if self.model: + return self.model + raise Exception( + f"{TEST_SETUP_ERROR} не определено поле model в " + f"классе {__class__}.", + ) + + @property + def future_obj_id(self): + """ + Предсказывает id будущего объекта. + + Для использования в тестировании CRUD через url, + в котором указывается id записи. + """ + return self.get_model().objects.count() + 1 + + def get_model_schema(self): + """ + Возвращает схему тестирования. + + Переопределение данного метода не рекомендуется. Для динамического + добавления какого-то поля в схему создания или изменения объекта + переопределите методы "get_correct_create_schema()" или + "get_correct_update_schema". + """ + if self.model_schema: + return self.model_schema + raise Exception( + f"{TEST_SETUP_ERROR} не определено поле model_schema в классе" + f" {self.__class__.__name__}.", + ) + + def _get_schema_key_value(self, key_name: str, schema: dict | None = None): + schema = schema or self.get_model_schema() + if value := schema.get(key_name, None): + return value + raise Exception( + f"{TEST_SETUP_ERROR} в предоставленной схеме '{self.get_model()}' " + f"класса '{self.__class__.__name__} 'отсутствует ключ " + f"'{key_name}'.", + ) + + def get_correct_create_schema(self): + """ + Возвращает значение ключа "correct_create" схемы тестирования. + + Переопределите этот метод для изменения логики или динамического + добавления какого-то поля в схему создания объекта. + """ + schema = copy.copy(self._get_schema_key_value("correct_create")) + self.fill_foreign_keys(schema) + return schema + + def get_correct_update_schema(self): + """ + Возвращает значение ключа "correct_update" схемы тестирования. + + Переопределите этот метод для изменения логики или динамического + добавления какого-то поля в схему изменения объекта. + """ + schema = copy.copy(self._get_schema_key_value("correct_update")) + self.fill_foreign_keys(schema) + return schema + + def get_must_not_be_admitted_schemas(self): + """ + Возвращает значение ключа "must_not_be_admitted" схемы тестирования. + + Переопределите, если нужна другая логика. + """ + return self._get_schema_key_value("must_not_be_admitted") + + def get_must_be_admitted_schema(self): + """ + Возвращает значение ключа "must_be_admitted" схемы тестирования. + + Переопределите, если нужна другая логика. + """ + return self._get_schema_key_value("must_be_admitted") + + def create(self, **kwargs): + """ + Создает объект в БД. + + Параметр kwargs - словарь с парами "поле-значение". + """ + if self.model_factory: + return self.model_factory.create(**kwargs) + obj = self.model.objects.create(**kwargs) + obj.save() + return obj + + def _post(self, url, **kwargs): + """Выполняет POST-запрос.""" + self.client.force_login(self.superuser) + return self.client.post(url, kwargs) + + def try_to_create_via_url(self, url, **kwargs): + """ + Пытается создать объект через url. + + Параметр kwargs - словарь с парами "поле-значение" для тела + POST-запроса. + """ + self.replace_foreign_keys_and_bools(kwargs) + try: + return self._post(url, **kwargs) + except Exception as e: + raise AssertionError( + f"При создании объекта модели" + f" {self.get_model().__name__} " + f"путем пост-запроса на адрес '{url}' c " + f"корректными данными возникает исключение '{e}'. " + f"Использовались следующие данные: {kwargs}", + ) + + @staticmethod + def replace_foreign_keys_and_bools( + fields_kwargs: dict, + replace_bools: bool = True, + ): + """ + Выполняет замену foreign keys и булевых переменных. + + При создании или изменении объектов через url меняет ссылки на + объекты БД на их id для корректного выполнения запроса. В этих же + целях по умолчанию bool-значения полей меняются на строковые: True + на "on", False - на пустую строку. + """ + for key, value in fields_kwargs.items(): + if isinstance(value, Model): + fields_kwargs[key] = value.id + elif replace_bools and isinstance(value, bool): + fields_kwargs[key] = ("", "on")[value] + + @staticmethod + def fill_foreign_keys(fields_kwargs: dict): + """ + Заменяет в схеме тестирования поля foreign keys. + + Заменяет ссылки на модели ссылками на конкретные объекты + модели в полях ForeignKey. + """ + for key, value in fields_kwargs.items(): + if type(value) is type(Model): + fk_obj = value.objects.first() # noqa + fields_kwargs[key] = fk_obj + + def try_to_update_via_url(self, url, **kwargs): + """ + Пытается изменить объект через url. + + Параметр kwargs - словарь с парами "поле-значение" для тела + POST-запроса. + """ + self.replace_foreign_keys_and_bools(kwargs) + try: + return self._post(url, **kwargs) + except Exception as e: + raise AssertionError( + f"При изменении объекта модели" + f" {self.get_model().__name__} " + f"путем пост-запроса на адрес '{url}' " + f"возникает исключение '{e}'. " + f"Использовались следующие данные: {kwargs}", + ) + + def try_to_delete_via_url(self, url, **kwargs): + """ + Пытается удалить объект через url. + + Параметр kwargs - словарь с парами "поле-значение" для тела + POST-запроса. + """ + try: + return self._post(url, **kwargs) + except Exception as e: + raise AssertionError( + f"При удалении объекта модели" + f" {self.get_model().__name__} " + f"путем пост-запроса на адрес '{url}' возникает исключение " + f"'{e}'.", + ) + + @transaction.atomic() + def try_to_create(self, **kwargs): + """ + Пытается создать объект непосредственно в БД. + + Параметр kwargs - словарь с парами "поле-значение". + """ + str_kwargs = f"{kwargs}" + try: + obj = self.create(**kwargs) + obj.full_clean() + return obj + except Exception as e: + raise AssertionError( + f"При создании объекта модели {self.get_model().__name__} с " + f"корректными данными возникает исключение '{e}'. " + f"Использовались следующие данные: {str_kwargs}", + ) + + @transaction.atomic() + def try_to_delete(self, obj_id): + """ + Пытается удалить объект с id "obj_id" непосредственно из БД. + + Для объектов, идентифицируемых по другому полю, переопределите этот + метод. + """ + model = self.get_model() + try: + obj = get_object_or_404(model, id=obj_id) + obj.delete() + except Exception as e: + raise AssertionError( + f"При попытке удаления из БД записи о заведомо существующем " + f"объекте модели {self.get_model().__name__} " + f"возникает исключение '{e}'. " + f"Использовались следующие данные: id={obj_id}", + ) + + @staticmethod + def update(obj: Model, **kwargs): + """Изменяет объект obj непосредственно в БД.""" + for field, value in kwargs.items(): + if hasattr(obj, field): + setattr(obj, field, value) + obj.full_clean() + obj.save() + + def try_to_update(self, obj: Model, **kwargs): + """ + Пытается изменить объект obj непосредственно в БД. + + Параметр kwargs - словарь с парами "поле-значение". + """ + str_kwargs = f"{kwargs}" + try: + self.update(obj, **kwargs) + except Exception as e: + raise AssertionError( + f"При обновлении объекта модели " + f"'{self.get_model().__name__}' с " + f"корректными данными возникает исключение '{e}'." + f"Использовались следующие данные: {str_kwargs}", + ) + + def objects_count_test(self, estimated_count: int, err_msg: str): + """Тестирует на количество объектов.""" + count = self.get_model().objects.count() + self.assertEqual(count, estimated_count, msg=err_msg) + + def is_exists(self, **obj_kwargs): + """Проверяет, существует ли объект.""" + return self.get_model().objects.filter(**obj_kwargs).exists() + + def assert_object_exist(self, err_msg: str, **obj_kwargs): + """Тестирует на существование объекта.""" + if obj_kwargs.get("password", None): + obj_kwargs.pop("password") + self.assertTrue(self.is_exists(**obj_kwargs), err_msg) + + def assert_object_not_exist(self, err_msg: str, **obj_kwargs): + """Тестирует на несуществование объекта.""" + if obj_kwargs.get("password", None): + obj_kwargs.pop("password") + self.assertFalse(self.is_exists(**obj_kwargs), err_msg) + + def correct_create_tests( + self, + model_correct_schema: dict | None = None, + url: str | None = None, + **additional_url_kwargs, + ): + """ + Основной метод тестирования на корректное создание объекта. + + Для тестирования через url - передайте соответствующий параметр и + (при необходимости) additional_url_kwargs - словарь с дополнительным + контекстом для POST-запроса. Параметр model_correct_schema + предусмотрен для случаев, если по каким-то причинам при вызове + метода необходимо изменить схему "correct_create", использующуюся + по умолчанию. Если параметр url не передать, будет тестироваться + изменение путем программного обращения к БД. + """ + schema = model_correct_schema or self.get_correct_create_schema() + initial_objects_count = self.get_model().objects.count() + via_url = "" + if url: + if additional_url_kwargs.get("diagnosis", None): + schema.pop("diagnosis") + self.try_to_create_via_url(url, **schema, **additional_url_kwargs) + via_url = f" через POST-запрос по адресу: {url}" + else: + self.try_to_create(**schema) + + err_msg = ( + f"При создании объекта модели " + f"'{self.get_model().__name__}'{via_url} новое количество " + f"объектов в БД не соответствует ожидаемому. Ожидаемое " + f"количество объектов в БД: {initial_objects_count + 1}." + ) + self.objects_count_test(initial_objects_count + 1, err_msg=err_msg) + + err_msg = ( + f"После создания объекта модели " + f"'{self.get_model().__name__}'{via_url} объект с данными, " + f"использованным для создания, не обнаруживается в БД. " + f"Объект создавался со следующими данными: {schema}." + ) + self.assert_object_exist(err_msg, **schema) + + def correct_update_tests( + self, + model_upd_schema: dict | None = None, + url: str | None = None, + **additional_url_kwargs, + ): + """ + Основной метод тестирования на корректное изменение объекта. + + Для тестирования через url - передайте соответствующий параметр и + (при необходимости) additional_url_kwargs - словарь с дополнительным + контекстом для POST-запроса. Параметр model_correct_schema + предусмотрен для случаев, если по каким-то причинам при вызове + метода необходимо изменить схему "correct_update", использующуюся + по умолчанию. Если параметр url не передать, будет тестироваться + изменение путем программного обращения к БД. + """ + cr_schema = self.get_correct_create_schema() + upd_schema = model_upd_schema or self.get_correct_update_schema() + obj = self.try_to_create(**cr_schema) + via_url = "" + if url: + if additional_url_kwargs.get("diagnosis", None): + upd_schema.pop("diagnosis") + self.try_to_update_via_url( + url, + **upd_schema, + **additional_url_kwargs, + ) + via_url = f" через POST-запрос по адресу: {url}" + else: + self.try_to_update(obj, **upd_schema) + err_msg = ( + f"После обновления объекта модели " + f"{self.get_model().__name__}{via_url}, объект с корректными " + f"данными, использованным для обновления, не обнаруживается в БД. " + f"Объект обновлялся со следующими данными: {upd_schema}." + ) + self.assert_object_exist(err_msg, **upd_schema) + + def correct_delete_tests( + self, + url: str | None = None, + **additional_url_kwargs, + ): + """ + Основной метод тестирования на корректное удаление объекта. + + Для тестирования через url - передайте соответствующий параметр и + (при необходимости) additional_url_kwargs - словарь с дополнительным + контекстом для POST-запроса. Параметр model_correct_schema + предусмотрен для случаев, если по каким-то причинам при вызове + метода необходимо изменить схему "correct_update", использующуюся + по умолчанию. Если параметр url не передать, будет тестироваться + изменение путем программного обращения к БД. + """ + cr_schema = self.get_correct_create_schema() + obj = self.try_to_create(**cr_schema) + initial_objects_count = self.get_model().objects.count() + id = obj.id + via_url = "" + if url: + self.try_to_delete_via_url(url, **additional_url_kwargs) + via_url = f" через POST-запрос по адресу: {url}" + else: + self.try_to_delete(id) + err_msg = ( + f"После удаления объекта модели " + f"{self.get_model().__name__}{via_url} количество объектов в БД " + f"не соответствует ожидаемому." + ) + self.objects_count_test(initial_objects_count - 1, err_msg=err_msg) + + def update_field(self, obj: Model, url: str | None = None, **field_kwargs): + """Обновляет одно поле (для теста невалидных значений).""" + if url: + schema = self.get_correct_update_schema() + schema.update(field_kwargs) + self.try_to_update_via_url(url, **schema) + else: + self.update(obj, **field_kwargs) + + def _correct_field_test( + self, + obj: Model, + url: str | None = None, + **kwargs, + ): + """Обновляет одно поле (для теста валидных значений).""" + if url: + self.try_to_update_via_url(url, **kwargs) + else: + self.update(obj, **kwargs) + + @staticmethod + def _unpack_test_values(field, value): + """Распаковывает внутренние кортежи отдельного тестового значения.""" + res = [] + value = list(value) + msg = value.pop() + if any(tuple_val := tuple(isinstance(i, tuple) for i in value)): + tuple_index = (1, 0)[tuple_val[0]] + prior_val = value[0] if tuple_index else "" + post_val = value[-1] if (len(value) - 1) > tuple_index else "" + batch_value = [] + for item in value[tuple_index]: + batch_value.append( + { + "msg": msg, + "value": prior_val + item + post_val, + "field": field, + }, + ) + res.append(batch_value) + else: + res.append({"msg": msg, "value": value[0], "field": field}) + return res + + def _unpack_field_tests(self, test_item: dict): + """Распаковывает и формирует списки для каждой пары поле - значение.""" + fields = self._get_schema_key_value("fields", test_item) + value_set = self._get_schema_key_value("test_values", test_item) + if isinstance(fields, str): + fields = (fields,) + unpacked_tests = [] + for field in fields: + for value in value_set: + for unpacked_value in self._unpack_test_values(field, value): + unpacked_tests.append(unpacked_value) + return unpacked_tests + + @staticmethod + def _alter_tested_value_info(value: Any, force_value: bool = True) -> str: + """ + Формирует часть информационного сообщения теста. + + Отображает какое конкретно значение тестировалось. + """ + if value or force_value: + return f"Тестировавшееся значение: '{value}'" + return "" + + def _incorrect_field_msg_compile( + self, + field, + msg, + value=None, + force_value=True, + ): + """Формирует информационное сообщение теста НЕВАЛИДНОГО поля.""" + tested_value_info = self._alter_tested_value_info(value, force_value) + return ( + f"Проверьте, что при попытке сохранения в БД модели" + f" '{self.get_model().__name__}' с полем '{field}', " + f"содержащим невалидные данные ({msg}), " + f"вызывается исключение ValidationError. {tested_value_info}" + ).strip() + + def _incorrect_field_via_url_msg_compile( + self, + field, + msg, + url, + value=None, + force_value=True, + ): + """Формирует информационное сообщение url-теста НЕВАЛИДНОГО поля.""" + tested_value_info = self._alter_tested_value_info(value, force_value) + return ( + f"Проверьте, что POST-запрос по адресу '{url}' " + f"содержащий невалидные данные для поля '{field}' модели" + f" '{self.get_model().__name__}' ({msg}), " + f"возвращает ответ со статус-кодом 200(OK) или 302(FOUND) и " + f"объект в БД не изменяется." + f" {tested_value_info}" + ).strip() + + def _correct_field_msg_compile( + self, + field, + msg, + url, + value=None, + force_value=True, + ): + """Формирует информационное сообщение теста ВАЛИДНОГО поля.""" + tested_value_info = self._alter_tested_value_info(value, force_value) + if url: + return self._correct_field_via_url_msg_compile( + field, + msg, + url, + value, + force_value, + ) + return ( + f"Проверьте, что в поле '{field}' модели " + f"'{self.get_model().__name__}' допускается сохранение и не " + f"вызывает ошибки валидации такие валидные данные, как " + f"'{msg}'. {tested_value_info}" + ).strip() + + def _correct_field_via_url_msg_compile( + self, + field, + msg, + url, + value=None, + force_value=True, + ): + """Формирует информационное сообщение url-теста ВАЛИДНОГО поля.""" + tested_value_info = self._alter_tested_value_info(value, force_value) + return ( + f"Проверьте, что POST-запрос по адресу '{url}' " + f"содержащий такие валидные данные для поля '{field}' модели " + f"'{self.get_model().__name__}', как '{msg}', " + f"возвращает ответ со статус-кодом 200(OK) или 302(FOUND) и " + f"изменения сохраняются в БД. {tested_value_info}" + ).strip() + + def _incorrect_field_sub_test_via_url( + self, + url: str, + test: dict, + sub: bool = True, + **additional_url_kwargs, + ): + """ + Проводит отдельный тест поля через url. + + - Словарь test должен содержать ключи "field", "value", "msg". + - Параметр sub определяет, будет ли тест запускаться с помощью + subTest. В случае sub == True (по умолчанию) при падении одного + теста, остальные за пределами данного метода - продолжатся. + False - остановятся. + - Параметр additional_url_kwargs - дополнительный контекст для + POST-запроса. + """ + field, value, msg = test["field"], test["value"], test["msg"] + field_kwargs = {field: value} + schema = self.get_correct_update_schema() + schema.update(field_kwargs) + err_msg = ( + f"Проверьте, что POST-запрос по адресу '{url}', " + f"содержащий невалидные данные для поля {field} модели " + f"'{self.get_model().__name__}' ({msg}), " + f"возвращает ответ со статус-кодом '200(OK)' или '302(FOUND)'. " + f"{self._alter_tested_value_info(value, True)}" + ).strip() + response = self.try_to_update_via_url( + url, + **schema, + **additional_url_kwargs, + ) + self.assertIn( + response.status_code, + [HTTPStatus.OK, HTTPStatus.FOUND], + err_msg, + ) + err_msg = ( + f"В результате POST-запроса по адресу '{url}' " + f"содержащего невалидные данные для поля '{field}' модели " + f"'{self.get_model().__name__}' ({msg}), " + f"объект с указанными невалидными данными обнаруживается в " + f"БД. {self._alter_tested_value_info(value, True)}" + ).strip() + if sub: + with self.subTest(msg=err_msg): + self.assert_object_not_exist(msg, **schema) + else: + self.assert_object_not_exist(err_msg, **schema) + + def _incorrect_field_sub_test(self, test, obj, sub): + """ + Проводит тест с невалидным значением поля. + + Проводит отдельный тест невалидного значения поля через + попытку записи непосредственно в БД с помощью методов модели Django. + - Словарь test должен содержать ключи "field", "value", "msg". + - Параметр sub определяет, будет ли тест запускаться с помощью + subTest. В случае sub == True (по умолчанию) при падении одного + теста, остальные за пределами данного метода - продолжатся. + False - остановятся. + - Параметр additional_url_kwargs - дополнительный контекст для + POST-запроса. + """ + field = test["field"] + msg = test["msg"] + value = test["value"] + msg = self._incorrect_field_msg_compile(field, msg, value) + kwargs = {test["field"]: test["value"]} + + def _inner_assert(): + with transaction.atomic(): + self.assertRaises( + (ValidationError, ValueError), + self.update_field, + obj, + None, + **kwargs, + ) + + if sub: + with self.subTest(msg=msg): + _inner_assert() + else: + self.update_field(obj=obj, **kwargs) + # Приводим объект обратно в заведомо валидное состояние. + self.try_to_update(obj, **self.get_correct_update_schema()) + + def _get_field_tests_set( + self, + schemas: Iterable, + create_obj: bool = True, + ) -> tuple | tuple[tuple, Model]: + """ + Возвращает коллекцию всех тестов всех полей, указанных в схеме. + + - Параметр "schemas" - список всех тестов (т.е. значение ключа + "must_not_be_admitted" или "must_be_admitted"; + - Параметр "create_obj" - если равен True (по умолчанию), то метод + также создаст и вернет корректный объект, над которым впоследствии + можно проводить тесты поля. + """ + tests_set = [] + for test_item in schemas: + tests_set += self._unpack_field_tests(test_item) + if create_obj: + obj = self.try_to_create(**self.get_correct_update_schema()) + return tuple(tests_set), obj + return tuple(tests_set) + + def incorrect_field_tests_via_url(self, url: str, **additional_url_kwargs): + """ + Основной метод url-тестирования на некорректное значение поля. + + При необходимости передайте в параметре additional_url_kwargs - + словарь с дополнительным контекстом для POST-запроса. + """ + schemas = self.get_must_not_be_admitted_schemas() + tests_set, _ = self._get_field_tests_set(schemas, create_obj=True) + for batch_test in tests_set: + if isinstance(batch_test, dict): + self._incorrect_field_sub_test_via_url( + url, + batch_test, + **additional_url_kwargs, + ) + else: + for test in batch_test: + assertion_error = None + try: + self._incorrect_field_sub_test_via_url( + url, + test, + sub=False, + **additional_url_kwargs, + ) + except AssertionError as e: + err_msg = e.args[0] + with self.subTest(msg=err_msg): + assertion_error = e + raise e + finally: + if assertion_error: + break + + def incorrect_field_tests(self): + """ + Основной метод тестирования на некорректное значение поля. + + В отличие от остальных "основных" методов тестирования, не имеет + собственного параметра url. Для тестирования через url используйте + отдельный метод incorrect_field_tests_via_url(). + """ + schemas = self.get_must_not_be_admitted_schemas() + tests_set, obj = self._get_field_tests_set(schemas, create_obj=True) + for batch_test in tests_set: + if isinstance(batch_test, dict): + self._incorrect_field_sub_test( + test=batch_test, + obj=obj, + sub=True, + ) + else: + for test in batch_test: + msg = self._incorrect_field_msg_compile( + test["field"], + test["msg"], + test["value"], + ) + exception = None + try: + self._incorrect_field_sub_test( + test=test, + obj=obj, + sub=False, + ) + except Exception as e: + exception = e + finally: + with self.subTest(msg=msg): + with self.assertRaises(ValidationError): + if exception: + raise exception + continue + break + + def correct_field_tests(self, url: str | None = None, **kwargs): + """ + Основной метод тестирования ВАЛИДНОГО значения поля. + + Для тестирования через url передайте путь в параметре url/ + В параметре kwargs передайте словарь с дополнительными + преобразованиями для схемы модели и (или) дополнительным контентом для + POST-запроса. + """ + schema = self.get_must_be_admitted_schema() + correct_schema = self.get_correct_update_schema() + obj = self.try_to_create(**correct_schema) + correct_schema.update(kwargs) + tests_set = [] + for test_item in schema: + tests_set += self._unpack_field_tests(test_item) + for batch_test in tests_set: + if isinstance(batch_test, dict): + batch_test = (batch_test,) + for test in batch_test: + kwargs = {test["field"]: test["value"]} + msg = self._correct_field_msg_compile(**test, url=url) + update_schema = copy.copy(correct_schema) + update_schema.update(kwargs) + with self.subTest(msg=msg): + self._correct_field_test(obj, url, **update_schema) + self.assertTrue( + self.get_model().objects.filter(**kwargs).exists(), + msg=msg, + ) diff --git a/adaptive_hockey_federation/tests/command_test.py b/adaptive_hockey_federation/tests/command_test.py new file mode 100644 index 00000000..83034037 --- /dev/null +++ b/adaptive_hockey_federation/tests/command_test.py @@ -0,0 +1,46 @@ +import pytest +from main.data_factories.factories import TeamFactory +from main.models import Team +from users.factories import UserFactory + + +@pytest.fixture +def create_user(): + return UserFactory() + + +@pytest.fixture +def create_team(create_user): + return TeamFactory(curator=create_user) + + +@pytest.mark.django_db +def test_create_team(create_team): + assert create_team is not None, "Не удалось создать команду" + + +@pytest.mark.django_db +def test_read_team(create_team): + # TODO: Добавить проверку пермишенов для чтения команды + team_id = create_team.id + read_team = Team.objects.filter(pk=team_id).first() + assert read_team is not None, "Не удалось считать информацию о команде" + + +@pytest.mark.django_db +def test_update_team(create_team): + # TODO: Добавить проверку пермишенов для обновления команды + new_team_name = "Updated Team" + create_team.name = new_team_name + create_team.save() + updated_team = Team.objects.get(pk=create_team.pk) + assert updated_team.name == new_team_name, "Не удалось обновить команду" + + +@pytest.mark.django_db +def test_delete_team(create_team): + # TODO: Добавить проверку пермишенов для удаления команды + team_id = create_team.id + create_team.delete() + deleted_team = Team.objects.filter(pk=team_id).first() + assert deleted_team is None, "Не удалось удалить команду" diff --git a/adaptive_hockey_federation/tests/crud_test.py b/adaptive_hockey_federation/tests/crud_test.py new file mode 100644 index 00000000..67c84a12 --- /dev/null +++ b/adaptive_hockey_federation/tests/crud_test.py @@ -0,0 +1,916 @@ +from typing import Any + +import pytest +from core.constants import Role +from main.models import ( + City, + Diagnosis, + DisciplineLevel, + DisciplineName, + Nosology, + Player, + StaffMember, + StaffTeamMember, + Team, +) +from tests.base import ModelTestBaseClass +from tests.model_schemas.city import CITY_MODEL_TEST_SCHEMA +from tests.model_schemas.diagnosis import DIAGNOSIS_MODEL_TEST_SCHEMA +from tests.model_schemas.discipline_level import ( + DISCIPLINE_LEVEL_MODEL_TEST_SCHEMA, +) +from tests.model_schemas.discipline_name import ( + DISCIPLINE_NAME_MODEL_TEST_SCHEMA, +) +from tests.model_schemas.group import GROUP_MODEL_TEST_SCHEMA +from tests.model_schemas.nosology import NOSOLOGY_MODEL_TEST_SCHEMA +from tests.model_schemas.player import PLAYER_MODEL_TEST_SCHEMA +from tests.model_schemas.staff_member import STAFF_MEMBER_MODEL_TEST_SCHEMA +from tests.model_schemas.staff_team_member import ( + STAFF_TEAM_MEMBER_MODEL_TEST_SCHEMA, +) +from tests.model_schemas.team import TEAM_MODEL_TEST_SCHEMA +from tests.model_schemas.user import USER_MODEL_TEST_SCHEMA +from users.factories import UserFactory +from users.models import ProxyGroup, User + + +class UserCrudTest(ModelTestBaseClass): + """CRUD-тесты пользователя.""" + + model = User + model_schema = USER_MODEL_TEST_SCHEMA + model_factory = UserFactory + + def test_user_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_user_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_user_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + # def test_user_fields_admit_values(self): + # """Тест на допуск корректных значений напрямую через БД.""" + # self.correct_field_tests() + + def test_user_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_user_create_via_http(self): + """Тест на корректное создание через сайт.""" + self.correct_create_tests(url="/users/create/") + + def test_user_update_via_http(self): + """Тест на корректное изменение через сайт.""" + url = f"/users/{self.future_obj_id}/edit/" + self.correct_update_tests(url=url) + + def test_user_delete_via_http(self): + """Тест на корректное удаление через сайт.""" + url = f"/users/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url) + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_user_fields_validation_via_http(self): + # """Тест на валидацию некорректных значений при изменении через сайт.""" + # url = f"/users/{self.future_obj_id}/edit/" + # self.incorrect_field_tests_via_url(url=url) + + # def test_user_fields_admit_values_via_http(self): + # """Тест на допуск корректных значений при изменении через сайт.""" + # url = f"/users/{self.future_obj_id}/edit/" + # self.correct_field_tests(url=url) + + def test_user_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/users/user/add/") + + def test_user_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/users/user/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_user_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений при изменении через + # административную часть.""" + # url = f"/admin/users/user/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_user_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + self.client.force_login(self.superuser) + url = f"/admin/users/user/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_user_fields_admit_values_via_admin(self): + # """Тест на допуск корректных значений через административную часть.""" + # url = f"/admin/users/user/{self.future_obj_id}/change/" + # self.correct_field_tests(url=url) + + +class GroupCrudTest(ModelTestBaseClass): + """CRUD-тесты групп.""" + + model = ProxyGroup + model_schema = GROUP_MODEL_TEST_SCHEMA + + def test_group_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_group_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + def test_group_fields_validation(self): + """Тест на валидацию некорректных значений напрямую через БД.""" + self.incorrect_field_tests() + + def test_group_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_group_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_group_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/auth/group/add/") + + def test_group_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/auth/group/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + def test_group_fields_validation_via_admin(self): + """ + Тест на валидацию некорректных значений. + + Через административную часть. + """ + url = f"/admin/auth/group/{self.future_obj_id}/change/" + self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_group_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/auth/group/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_group_fields_admit_values_via_admin(self): + """Тест на допуск корректных значений через административную часть.""" + url = f"/admin/auth/group/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class CityCrudTest(ModelTestBaseClass): + """CRUD-тесты модели города.""" + + model = City + model_schema = CITY_MODEL_TEST_SCHEMA + + def test_city_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_city_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_city_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_city_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_city_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_city_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/city/add/") + + def test_city_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/city/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_city_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений через административную + # часть.""" + # url = f"/admin/main/city/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_city_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/city/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_city_fields_admit_values_via_admin(self): + """Тест на допуск корректных значений через административную часть.""" + url = f"/admin/main/city/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class DiagnosisCrudTest(ModelTestBaseClass): + """CRUD-тесты модели диагноза.""" + + model = Diagnosis + model_schema = DIAGNOSIS_MODEL_TEST_SCHEMA + + def test_diagnosis_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_diagnosis_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_diagnosis_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_diagnosis_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_diagnosis_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_diagnosis_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/diagnosis/add/") + + def test_diagnosis_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/diagnosis/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_diagnosis_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/diagnosis/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_diagnosis_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/diagnosis/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_diagnosis_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/diagnosis/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class NosologyCrudTest(ModelTestBaseClass): + """CRUD-тесты модели диагноза.""" + + model = Nosology + model_schema = NOSOLOGY_MODEL_TEST_SCHEMA + + def test_nosology_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_nosology_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_nosology_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_nosology_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_nosology_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_nosology_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/nosology/add/") + + def test_nosology_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/nosology/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_nosology_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/nosology/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_nosology_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/nosology/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_nosology_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/nosology/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class DisciplineNameCrudTest(ModelTestBaseClass): + """CRUD-тесты модели диагноза.""" + + model = DisciplineName + model_schema = DISCIPLINE_NAME_MODEL_TEST_SCHEMA + + def test_discipline_name_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_discipline_name_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_discipline_name_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_discipline_name_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_discipline_name_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_discipline_name_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/disciplinename/add/") + + def test_discipline_name_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/disciplinename/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_discipline_name_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/disciplinename/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_discipline_name_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/disciplinename/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_discipline_name_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/disciplinename/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class DisciplineLevelCrudTest(ModelTestBaseClass): + """CRUD-тесты модели уровня дисциплин.""" + + model = DisciplineLevel + model_schema = DISCIPLINE_LEVEL_MODEL_TEST_SCHEMA + + def test_discipline_level_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_discipline_level_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_discipline_level_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_discipline_level_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_discipline_level_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_discipline_level_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/disciplinelevel/add/") + + def test_discipline_level_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/disciplinelevel/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_discipline_level_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/disciplinelevel/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_discipline_level_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/disciplinelevel/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_discipline_level_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/disciplinelevel/{self.future_obj_id}/change/" + self.correct_field_tests(url=url) + + +class StaffMemberCrudTest(ModelTestBaseClass): + """CRUD-тесты сотрудника.""" + + model = StaffMember + model_schema = STAFF_MEMBER_MODEL_TEST_SCHEMA + + def test_staff_member_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_staff_member_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_staff_member_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + # def test_staff_member_fields_admit_values(self): + # """Тест на допуск корректных значений напрямую через БД.""" + # self.correct_field_tests() + + def test_staff_member_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_staff_member_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests(url="/admin/main/staffmember/add/") + + def test_staff_member_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/staffmember/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_staff_member_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/staffmember/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url(url=url, _save="Сохранить") + + def test_staff_member_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/staffmember/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_staff_member_fields_admit_values_via_admin(self): + # """Тест на допуск корректных значений полей через административную + # часть.""" + # url = f"/admin/main/staffmember/{self.future_obj_id}/change/" + # self.correct_field_tests(url=url) + + +@pytest.mark.skip(reason="Модель StuffTeamMember был исключен из админки") +class StaffTeamMemberCrudTest(ModelTestBaseClass): + """CRUD-тесты сотрудника команды.""" + + model = StaffTeamMember + model_schema = STAFF_TEAM_MEMBER_MODEL_TEST_SCHEMA + + def test_staff_team_member_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_staff_team_member_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + def test_staff_team_member_fields_validation(self): + """Тест на валидацию некорректных значений напрямую через БД.""" + self.incorrect_field_tests() + + def test_staff_team_member_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_staff_team_member_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_staff_team_member_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + schema = self.get_correct_create_schema() + schema["team"] = "1" + self.correct_create_tests( + schema, + url="/admin/main/staffteammember/add/", + ) + + def test_staff_team_member_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/staffteammember/{self.future_obj_id}/change/" + self.correct_update_tests(url=url, _save="Сохранить", team="1") + + def test_staff_team_member_fields_validation_via_admin(self): + """ + Тест на валидацию некорректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/staffteammember/{self.future_obj_id}/change/" + self.incorrect_field_tests_via_url(url=url, _save="Сохранить", team=1) + + def test_staff_team_member_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + url = f"/admin/main/staffteammember/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_staff_team_member_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/staffteammember/{self.future_obj_id}/change/" + self.correct_field_tests(url=url, team=1) + + # TODO: Раскомментировать и доработать все тесты на стаффтиммембера + # через сайт, поскольку вьюха работает некорректно в принципе: она + # запитана на модель StaffMember, в то время как должна быть + # задействована модель StaffTeamMember. Соответственно, если у нас + # один человек занимает две должности в команде, то: + # 1. Не отображаются обе записи + # 2. При удалении одной удаляются две, поскольку происходит касакадное + # удаление. + + # def test_staff_team_member_correct_create_via_http(self): + # """Тест на корректное создание через сайт.""" + # schema = self.get_correct_create_schema() + # schema["team"] = "1" + # self.correct_create_tests( + # schema, url="/staffs/create/" + # ) + # + # def test_staff_team_member_correct_update_via_http(self): + # """Тест на корректное изменение через сайт.""" + # url = f"/staffs/{self.future_obj_id}/edit/" + # self.correct_update_tests(url=url, _save="Сохранить", team="1") + # + # def test_staff_team_member_fields_validation_via_http(self): + # """Тест на валидацию некорректных значений полей через сайт.""" + # url = f"/staffs/{self.future_obj_id}/edit/" + # self.incorrect_field_tests_via_url( + # url=url, _save="Сохранить", team=1 + # ) + # + # def test_staff_team_member_delete_via_http(self): + # """Тест на корректное удаление через сайт.""" + # url = f"/staffs/{self.future_obj_id}/delete/" + # self.correct_delete_tests(url=url) + # + # def test_staff_team_member_fields_admit_values_via_http(self): + # """Тест на допуск корректных значений полей через сайт.""" + # url = f"/staffs/{self.future_obj_id}/edit/" + # self.correct_field_tests(url=url, team=1) + + +class TeamCrudTest(ModelTestBaseClass): + """CRUD-тесты команды.""" + + model = Team + model_schema = TEAM_MODEL_TEST_SCHEMA + user_agent: User | Any = None + admin_inlines_no_player_no_staff = { + "StaffTeamMember_team-TOTAL_FORMS": 0, + "StaffTeamMember_team-INITIAL_FORMS": 0, + "Player_team-TOTAL_FORMS": 0, + "Player_team-INITIAL_FORMS": 0, + } + + @classmethod + def setUpClass(cls) -> None: + """Классовый метод для базовой настройки всех тестов класса.""" + super().setUpClass() + cls.user_agent = UserFactory.create(role=Role.AGENT) + + def get_correct_create_schema(self): + """Метод на формирование корректной схемы создания объектов.""" + schema = super(TeamCrudTest, self).get_correct_create_schema() + schema["curator"] = self.user_agent + return schema + + def get_correct_update_schema(self): + """Метод на формирование корректной схемы обновления объектов.""" + schema = super().get_correct_update_schema() + schema["curator"] = self.user_agent + return schema + + def test_team_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_team_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_team_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + def test_team_fields_admit_values(self): + """Тест на допуск корректных значений напрямую через БД.""" + self.correct_field_tests() + + def test_team_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_team_create_via_http(self): + """Тест на корректное создание через сайт.""" + self.correct_create_tests(url="/teams/create/") + + def test_team_update_via_http(self): + """Тест на корректное изменение через сайт.""" + url = f"/teams/{self.future_obj_id}/edit/" + self.correct_update_tests(url=url) + + def test_team_delete_via_http(self): + """Тест на корректное удаление через сайт.""" + url = f"/teams/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url) + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_team_fields_validation_via_http(self): + # """Тест на валидацию некорректных значений полей через сайт.""" + # url = f"/teams/{self.future_obj_id}/edit/" + # self.incorrect_field_tests_via_url(url=url) + + def test_team_fields_admit_values_via_http(self): + """Тест на допуск корректных значений полей через сайт.""" + url = f"/teams/{self.future_obj_id}/edit/" + self.correct_field_tests(url=url) + + def test_team_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests( + url="/admin/main/team/add/", + **self.admin_inlines_no_player_no_staff, + ) + + def test_team_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/team/{self.future_obj_id}/change/" + self.correct_update_tests( + url=url, + _save="Сохранить", + **self.admin_inlines_no_player_no_staff, + ) + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_team_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/team/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url( + # url=url, _save="Сохранить", + # **self.admin_inlines_no_player_no_staff + # ) + + def test_team_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + self.client.force_login(self.superuser) + url = f"/admin/main/team/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + def test_team_fields_admit_values_via_admin(self): + """ + Тест на допуск корректных значений полей. + + Через административную часть. + """ + url = f"/admin/main/team/{self.future_obj_id}/change/" + self.correct_field_tests( + url=url, + **self.admin_inlines_no_player_no_staff, + ) + + +class PlayerCrudTest(ModelTestBaseClass): + """CRUD-тесты игрока.""" + + model = Player + model_schema = PLAYER_MODEL_TEST_SCHEMA + user_agent: User | Any = None + + admin_inlines_no_team_no_docs = { + "player_documemts-TOTAL_FORMS": 0, + "player_documemts-INITIAL_FORMS": 0, + "Player_team-TOTAL_FORMS": 0, + "Player_team-INITIAL_FORMS": 0, + } + + def test_player_correct_creation(self): + """Тест на корректное создание напрямую через БД.""" + self.correct_create_tests() + + def test_player_correct_update(self): + """Тест на корректное изменение напрямую через БД.""" + self.correct_update_tests() + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_player_fields_validation(self): + # """Тест на валидацию некорректных значений напрямую через БД.""" + # self.incorrect_field_tests() + + # def test_player_fields_admit_values(self): + # """Тест на допуск корректных значений напрямую через БД.""" + # self.correct_field_tests() + + def test_player_deletion(self): + """Тест на удаление объекта напрямую через БД.""" + self.correct_delete_tests() + + def test_player_create_via_http(self): + """Тест на корректное создание через сайт.""" + self.correct_create_tests( + url="/players/create/", + team=1, + nosology=1, + diagnosis=1, + ) + + def test_player_update_via_http(self): + """Тест на корректное изменение через сайт.""" + url = f"/players/{self.future_obj_id}/edit/" + self.correct_update_tests(url=url, team=1, nosology=1, diagnosis=1) + + def test_player_delete_via_http(self): + """Тест на корректное удаление через сайт.""" + url = f"/players/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url) + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_player_fields_validation_via_http(self): + # """Тест на валидацию некорректных значений полей через сайт.""" + # url = f"/players/{self.future_obj_id}/edit/" + # self.incorrect_field_tests_via_url(url=url, team=1) + + # def test_player_fields_admit_values_via_http(self): + # """Тест на допуск корректных значений полей через сайт.""" + # url = f"/players/{self.future_obj_id}/edit/" + # self.correct_field_tests(url=url, team=1) + + def test_player_correct_create_via_admin(self): + """Тест на корректное создание через административную часть.""" + self.correct_create_tests( + url="/admin/main/player/add/", + **self.admin_inlines_no_team_no_docs, + team=1, + nosology=1, + diagnosis=1, + ) + + def test_player_correct_update_via_admin(self): + """Тест на корректное изменение через административную часть.""" + url = f"/admin/main/player/{self.future_obj_id}/change/" + self.correct_update_tests( + url=url, + _save="Сохранить", + **self.admin_inlines_no_team_no_docs, + team=1, + nosology=1, + diagnosis=1, + ) + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_player_fields_validation_via_admin(self): + # """Тест на валидацию некорректных значений полей через + # административную часть.""" + # url = f"/admin/main/player/{self.future_obj_id}/change/" + # self.incorrect_field_tests_via_url( + # url=url, + # team=1, + # _save="Сохранить", + # **self.admin_inlines_no_team_no_docs, + # ) + + def test_player_delete_via_admin(self): + """Тест на корректное удаление через административную часть.""" + self.client.force_login(self.superuser) + url = f"/admin/main/player/{self.future_obj_id}/delete/" + self.correct_delete_tests(url=url, post="yes") + + # TODO: Раскомментировать после согласования объема валидаций и + # доработки валидации полей модели. + + # def test_player_fields_admit_values_via_admin(self): + # """Тест на допуск корректных значений полей через административную + # часть.""" + # url = f"/admin/main/player/{self.future_obj_id}/change/" + # self.correct_field_tests( + # url=url, + # team=1, + # **self.admin_inlines_no_team_no_docs, + # ) + + +# TODO: Сделать тест модели Document. +# TODO: Cделать тесты приложения Games, по нему у нас вообще ничего нет. +# TODO: Аналогично для Competitions и Unloads. +# TODO: Не очень понял, зачем нам файл сommand_test. +# TODO: там по сути 4 CRUD теста для команд, +# TODO: которые у нас уже есть в этом файле. +# TODO: Нужно решить, что мы делаем с тестами админки: +# TODO: Либо удаляем, либо дописываем. То же самое с тестами на валидацию. +# TODO: Соответственно, когда мы решим, в каком направлении мы двигаемся diff --git a/adaptive_hockey_federation/tests/fixture_user.py b/adaptive_hockey_federation/tests/fixture_user.py new file mode 100644 index 00000000..67d01463 --- /dev/null +++ b/adaptive_hockey_federation/tests/fixture_user.py @@ -0,0 +1,49 @@ +import pytest +from core.constants import Role + +test_password = "admin" +test_name = "admin" +test_lastname = "admin" +test_role_admin = Role.SUPERUSER +test_role_user = "user" +test_email = "admin@admin.ru" + + +@pytest.fixture +def user(django_user_model): + return django_user_model.objects.create_user( + password=test_password, + first_name=test_name, + last_name=test_lastname, + role=test_role_admin, + email=test_email, + ) + + +@pytest.fixture +def user_client(user, client): + client.force_login(user) + return client + + +@pytest.fixture +def adminuser(djangousermodel): + admin = djangousermodel.objects.createsuperuser( + first_name="admin", + email="admin@admin.com", + password="admin", + ) + admin.isstaff = True + admin.issuperuser = True + admin.save() + return admin + + +@pytest.fixture +def moderatoruser(djangousermodel): + moderator = djangousermodel.objects.createuser( + first_name="moderator", + email="moderator@moderator.com", + password="moderator", + ) + return moderator diff --git a/adaptive_hockey_federation/tests/model_schemas/__init__.py b/adaptive_hockey_federation/tests/model_schemas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/tests/model_schemas/city.py b/adaptive_hockey_federation/tests/model_schemas/city.py new file mode 100644 index 00000000..906624e0 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/city.py @@ -0,0 +1,37 @@ +from tests.model_schemas.fields_validation_schemas import ( + CORRECT_CITY_NAMES, + CORRECT_CREATE, + CORRECT_UPDATE, + FIGURES_AND_LETTERS, + FIGURES_ONLY, + LONG_256, + LONGER_THEN_256, + NOT_CYR, + NULL, +) + +CITY_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Городкоторогонет", + }, + CORRECT_UPDATE: { + "name": "Новыйгородкоторогонет", + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONGER_THEN_256, + NULL, + NOT_CYR, + FIGURES_ONLY, + ), + }, + ), + "must_be_admitted": ( + { + "fields": "name", + "test_values": (LONG_256, FIGURES_AND_LETTERS, CORRECT_CITY_NAMES), + }, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/diagnosis.py b/adaptive_hockey_federation/tests/model_schemas/diagnosis.py new file mode 100644 index 00000000..efd6d482 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/diagnosis.py @@ -0,0 +1,44 @@ +from main.models import Nosology +from tests.model_schemas.fields_validation_schemas import ( + CORRECT_CREATE, + CORRECT_UPDATE, + FIGURES_AND_LETTERS, + FIGURES_ONLY, + LONG_256, + LONGER_THEN_256, + NULL, + PUNCTUATION_MARKS_ONLY, + THE_ONLY_LETTER, +) + +DIAGNOSIS_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Диагнозкоторогонет", + "nosology": Nosology, + }, + CORRECT_UPDATE: { + "name": "Новыйдиагнозкоторогонет", + "nosology": Nosology, + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONGER_THEN_256, + NULL, + FIGURES_ONLY, + PUNCTUATION_MARKS_ONLY, + THE_ONLY_LETTER, + ), + }, + ), + "must_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONG_256, + FIGURES_AND_LETTERS, + ), + }, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/discipline_level.py b/adaptive_hockey_federation/tests/model_schemas/discipline_level.py new file mode 100644 index 00000000..ff9d095a --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/discipline_level.py @@ -0,0 +1,42 @@ +from main.models import DisciplineName +from tests.model_schemas.fields_validation_schemas import ( + ALL_LETTERS, + CORRECT_CREATE, + CORRECT_UPDATE, + FIGURES_AND_LETTERS, + LONG_256, + LONGER_THEN_256, + NULL, + PUNCTUATION_MARKS_ONLY, +) + +DISCIPLINE_LEVEL_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Наименование дисциплины или статуса дисциплины", + "discipline_name": DisciplineName, + }, + CORRECT_UPDATE: { + "name": "Новое наименование дисциплины или статуса дисциплины", + "discipline_name": DisciplineName, + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONGER_THEN_256, + NULL, + PUNCTUATION_MARKS_ONLY, + ), + }, + ), + "must_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONG_256, + FIGURES_AND_LETTERS, + ALL_LETTERS, + ), + }, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/discipline_name.py b/adaptive_hockey_federation/tests/model_schemas/discipline_name.py new file mode 100644 index 00000000..024e18b3 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/discipline_name.py @@ -0,0 +1,3 @@ +from tests.model_schemas.generics import SIMPLE_UNIQUE_NAME_MODEL_TEST_SCHEMA + +DISCIPLINE_NAME_MODEL_TEST_SCHEMA = SIMPLE_UNIQUE_NAME_MODEL_TEST_SCHEMA diff --git a/adaptive_hockey_federation/tests/model_schemas/fields_validation_schemas.py b/adaptive_hockey_federation/tests/model_schemas/fields_validation_schemas.py new file mode 100644 index 00000000..b0410192 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/fields_validation_schemas.py @@ -0,0 +1,140 @@ +CORRECT_CREATE = "correct_create" +CORRECT_UPDATE = "correct_update" + + +def get_range_string(start_symbol: str, end_symbol: str) -> str: + return "".join( + [chr(i) for i in range(ord(start_symbol), ord(end_symbol) + 1)], + ) + + +RUS_STRINGS = get_range_string("а", "я") + "ё" +RUS_CAPS = RUS_STRINGS.upper() +LATIN_STRINGS = get_range_string("a", "z") +LATIN_CAPS = LATIN_STRINGS.upper() +ALL_LETTERS = ( + (RUS_STRINGS, RUS_CAPS, LATIN_STRINGS, LATIN_CAPS), + "любые буквы русского и (или) латинского алфавита", +) +ALL_RUS_LETTERS_IN_FIO = ( + RUS_STRINGS.capitalize(), + "любые буквы русского алфавита", +) +VERY_LONG_TEXT = "Lorem ipsum dolor sit amet... Съешь еще этих мягких " * 1000 +ALL_LOWER = ("василий", "начало со строчной буквы") +ALL_CAPS = ("ВАСИЛИЙ", "строка полностью из прописных букв") +SPACES = ("Салтыков Щедрин", "наличие хотя бы одного пробела") +TWO_OR_MORE_SPACES = ("Алибаба олгы моглы", "наличие двух и более пробелов") +DOUBLE_LAST_NAME = ("Петров-Водкин", "двойная фамилия через дефис") +LOWER_SECOND_LAST_NAME = ("Петров-водкин", "вторая фамилия со строчной буквы") +DOUBLE_PATRONYMIC = ("Алимамбек оглы", "двойное отчество через пробел") +MIDDLE_CAP = ("ВаСилий", "прописная буква не в начале слова") +NOT_CYR = ("Gennadiy", "некириллические символы") +FIGURES_AND_LETTERS = ("Пётр1", "цифры наряду с буквами") +LONGER_THEN_256 = (("а" * 257).capitalize(), "строка длиннее 256 символов") +LONGER_THEN_150 = (("а" * 151).capitalize(), "строка длиннее 150 символов") +LONG_256 = (("а" * 256).capitalize(), "строка длиной до 256 символов") +LONG_150 = (("а" * 150).capitalize(), "строка длиной до 150 символов") +FIGURES_ONLY = ("1234567890", "значение, состоящее только из " "цифр") +INTEGER = (1, "числовое значение поля") +FLOAT = (1.25, "дробное значение поля") +ZERO = (0, "нулевое значение поля") +NEGATIVE = (-1, "отрицательное значение поля") +NONE = (None, "пустое значение поля") +NULL = ("", "значение поля, состоящее из пустой строки") +PUNCTUATION_MARKS_EXCEPT_HYPHEN = ( + "Васи", + tuple("~@#$%^&*()`.,\\{}\"[]<>/*+:;|!№?='"), + "лий", + "наличие символов пунктуации", +) +PUNCTUATION_MARKS = ( + "Васи", + tuple("-~@#$%^&*()`.,\\{}\"[]<>/*+:;|!№?='"), + "лий", + "наличие символов пунктуации", +) +PUNCTUATION_MARKS_ONLY = ( + '!"№;%:?()_', + "строка, состоящая только из знаков препинания", +) +SPACES_ONLY = (" ", "строка, состоящая из одних пробелов") + +THE_ONLY_LETTER = (tuple("ФFгg"), "строка, состоящая из единственной буквы") + +THE_ONLY_CYR_LETTER = ( + tuple("АБЯЁ"), + "строка, состоящая из одной русской буквы", +) + +THE_ONLY_LATIN_LETTER = ( + tuple("ABYZ"), + "строка, состоящая из одной латинской буквы", +) + +INCORRECT_EMAIL = ( + "", + ("adfasdf.lkj", "неправ@иль.но", "дажеблизконеемайл"), + "", + "невалидный адрес электронной почты", +) +UNFORESEEN_ROLE = ("Повелительмух", "непредусмотренная роль пользователя") +INCORRECT_PHONE = ( + ( + "+7 5f5 555 55 55", + "+7 555 555-55-55", + "+7 5555 555 55 55", + "+7 555 5555 55 55", + "+6 555 555 55 55", + "-7 555 555 55 55", + "+7_555_555 55 55", + "+7 xxx xxx xx xx", + "+Д 555 555 55 55", + "+А БРА КАД-АБ-РА", + "8_926_555@ya.ya", + "8_926_555@55.55", + ), + "номер телефона в неверном формате", +) +CORRECT_PHONE = ( + ( + "+7 955 555-55-54", + "+7 900 000-00-00", + "+7 959 111-11-11", + "+7 987 654-32-10", + ), + "правильный номер телефона", +) +CORRECT_CITY_NAMES = ( + ( + "г. Город-через-Дефис", + "пгт. Поселок Городского Типа", + "пос. им. В.И. Ленина", + "Арзамас-16", + ), + "сложные топонимы в названии города", +) + +INCORRECT_DOC_ID = ( + ( + "Паспорт 30 01 023456", + "Паспорт АБРА КАДАБР", + "ПаспАрт 9898 989898", + "СвидетельствА о рождении I-ML 656565", + "Абракадабрааб р акадабра I-ML 656565", + "Свидетельство о брожении I-ML 656565", + ), + "неверный формат документа, удостоверяющего личность", +) + +CORRECT_DOC_ID = ( + ( + "Паспорт 3001 023456", + "Свидетельство о рождении I-ML 656565", + "Свидетельство о рождении II-ML 656565", + ), + "документ, удостоверяющий личность, в корректном формате", +) + +INCORRECT_GENDER_CHOICE = ("Оно", "некорректный пол") +INCORRECT_PLAYER_POSITION = ("Зритель", "некорректная позиция игрока") diff --git a/adaptive_hockey_federation/tests/model_schemas/generics.py b/adaptive_hockey_federation/tests/model_schemas/generics.py new file mode 100644 index 00000000..1e36a40d --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/generics.py @@ -0,0 +1,43 @@ +from tests.model_schemas.fields_validation_schemas import ( + ALL_LETTERS, + CORRECT_CREATE, + CORRECT_UPDATE, + FIGURES_AND_LETTERS, + FIGURES_ONLY, + LONG_256, + LONGER_THEN_256, + NULL, + PUNCTUATION_MARKS_ONLY, + THE_ONLY_LETTER, +) + +SIMPLE_UNIQUE_NAME_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Какоетоимя", + }, + CORRECT_UPDATE: { + "name": "Какоетоновоеимя", + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONGER_THEN_256, + NULL, + FIGURES_ONLY, + PUNCTUATION_MARKS_ONLY, + THE_ONLY_LETTER, + ), + }, + ), + "must_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONG_256, + FIGURES_AND_LETTERS, + ALL_LETTERS, + ), + }, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/group.py b/adaptive_hockey_federation/tests/model_schemas/group.py new file mode 100644 index 00000000..a968a3f6 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/group.py @@ -0,0 +1,26 @@ +from tests.model_schemas.fields_validation_schemas import ( + CORRECT_CREATE, + CORRECT_UPDATE, + LONG_150, + LONGER_THEN_150, + NULL, +) + +GROUP_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Группа в полосатых купальниках", + }, + CORRECT_UPDATE: { + "name": "Группа крови на рукаве", + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + LONGER_THEN_150, + NULL, + ), + }, + ), + "must_be_admitted": ({"fields": "name", "test_values": (LONG_150,)},), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/nosology.py b/adaptive_hockey_federation/tests/model_schemas/nosology.py new file mode 100644 index 00000000..739e617c --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/nosology.py @@ -0,0 +1,3 @@ +from tests.model_schemas.generics import SIMPLE_UNIQUE_NAME_MODEL_TEST_SCHEMA + +NOSOLOGY_MODEL_TEST_SCHEMA = SIMPLE_UNIQUE_NAME_MODEL_TEST_SCHEMA diff --git a/adaptive_hockey_federation/tests/model_schemas/player.py b/adaptive_hockey_federation/tests/model_schemas/player.py new file mode 100644 index 00000000..fd6ef7aa --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/player.py @@ -0,0 +1,99 @@ +from main.models import Diagnosis, DisciplineLevel, DisciplineName +from tests.model_schemas.fields_validation_schemas import ( + ALL_CAPS, + ALL_LOWER, + ALL_RUS_LETTERS_IN_FIO, + CORRECT_CREATE, + CORRECT_DOC_ID, + CORRECT_UPDATE, + DOUBLE_LAST_NAME, + DOUBLE_PATRONYMIC, + FIGURES_AND_LETTERS, + INCORRECT_DOC_ID, + INCORRECT_GENDER_CHOICE, + INCORRECT_PLAYER_POSITION, + LONGER_THEN_256, + LOWER_SECOND_LAST_NAME, + MIDDLE_CAP, + NEGATIVE, + NOT_CYR, + NULL, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + SPACES, + THE_ONLY_CYR_LETTER, + TWO_OR_MORE_SPACES, +) + +PLAYER_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Василий", + "surname": "Иванович", + "patronymic": "Петров", + "diagnosis": Diagnosis, + "discipline_name": DisciplineName, + "discipline_level": DisciplineLevel, + "birthday": "2010-03-25", + "gender": "Мужской", + "level_revision": "Тестовый уровень ревизии.", + "position": "Поплавок", + "number": 45, + "is_captain": False, + "is_assistent": False, + "identity_document": "Паспорт 3030 303030", + }, + CORRECT_UPDATE: { + "name": "Бурямглоюнебокроетвихриснежныекрутятокакзверьоназавоет", + "surname": "Съешьещеэтихмягкихфранцузскихбулочекдавыпейчаюев", + "patronymic": "Кракозябробормоглототроглодитобрандашмыгович", + "diagnosis": Diagnosis, + "discipline_name": DisciplineName, + "discipline_level": DisciplineLevel, + "birthday": "2009-03-25", + "gender": "Женский", + "level_revision": "Какой-то другой тестовый уровень ревизии.", + "position": "Нападающий", + "number": 41, + "is_captain": True, + "is_assistent": True, + "identity_document": "Паспорт 4040 404040", + }, + "must_not_be_admitted": ( + { + "fields": ("name", "surname", "patronymic"), + "test_values": ( + ALL_LOWER, + ALL_CAPS, + MIDDLE_CAP, + NOT_CYR, + FIGURES_AND_LETTERS, + LONGER_THEN_256, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + ), + }, + { + "fields": ( + "name", + "surname", + ), + "test_values": (NULL,), + }, + { + "fields": "surname", + "test_values": (LOWER_SECOND_LAST_NAME, SPACES), + }, + {"fields": "patronymic", "test_values": (TWO_OR_MORE_SPACES,)}, + {"fields": "identity_document", "test_values": (INCORRECT_DOC_ID,)}, + {"fields": "gender", "test_values": (INCORRECT_GENDER_CHOICE,)}, + {"fields": "position", "test_values": (INCORRECT_PLAYER_POSITION,)}, + {"fields": "number", "test_values": (NEGATIVE,)}, + ), + "must_be_admitted": ( + { + "fields": ("surname", "name", "patronymic"), + "test_values": (THE_ONLY_CYR_LETTER, ALL_RUS_LETTERS_IN_FIO), + }, + {"fields": "surname", "test_values": (DOUBLE_LAST_NAME,)}, + {"fields": "patronymic", "test_values": (DOUBLE_PATRONYMIC,)}, + {"fields": "identity_document", "test_values": (CORRECT_DOC_ID,)}, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/staff_member.py b/adaptive_hockey_federation/tests/model_schemas/staff_member.py new file mode 100644 index 00000000..e8094147 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/staff_member.py @@ -0,0 +1,71 @@ +from tests.model_schemas.fields_validation_schemas import ( + ALL_CAPS, + ALL_LOWER, + CORRECT_CREATE, + CORRECT_PHONE, + CORRECT_UPDATE, + DOUBLE_LAST_NAME, + DOUBLE_PATRONYMIC, + FIGURES_AND_LETTERS, + INCORRECT_PHONE, + LONGER_THEN_256, + LOWER_SECOND_LAST_NAME, + MIDDLE_CAP, + NOT_CYR, + NULL, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + SPACES, + THE_ONLY_CYR_LETTER, + TWO_OR_MORE_SPACES, +) + +STAFF_MEMBER_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Василий", + "surname": "Иванович", + "patronymic": "Петров", + "phone": "+7 990 060-45-71", + }, + CORRECT_UPDATE: { + "name": "Бурямглоюнебокроетвихриснежныекрутятокакзверьоназавоет", + "surname": "Съешьещеэтихмягкихфранцузскихбулочекдавыпейчаюев", + "patronymic": "Кракозябробормоглототроглодитобрандашмыгович", + "phone": "+7 990 060-45-72", + }, + "must_not_be_admitted": ( + { + "fields": ("name", "surname", "patronymic"), + "test_values": ( + ALL_LOWER, + ALL_CAPS, + MIDDLE_CAP, + NOT_CYR, + FIGURES_AND_LETTERS, + LONGER_THEN_256, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + ), + }, + { + "fields": ( + "name", + "surname", + ), + "test_values": (NULL,), + }, + { + "fields": "surname", + "test_values": (LOWER_SECOND_LAST_NAME, SPACES), + }, + {"fields": "patronymic", "test_values": (TWO_OR_MORE_SPACES,)}, + {"fields": "phone", "test_values": (INCORRECT_PHONE,)}, + ), + "must_be_admitted": ( + { + "fields": ("surname", "name", "patronymic"), + "test_values": (THE_ONLY_CYR_LETTER,), + }, + {"fields": "surname", "test_values": (DOUBLE_LAST_NAME,)}, + {"fields": "patronymic", "test_values": (DOUBLE_PATRONYMIC,)}, + {"fields": "phone", "test_values": (CORRECT_PHONE,)}, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/staff_team_member.py b/adaptive_hockey_federation/tests/model_schemas/staff_team_member.py new file mode 100644 index 00000000..5184802c --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/staff_team_member.py @@ -0,0 +1,39 @@ +from main.models import StaffMember +from tests.model_schemas.fields_validation_schemas import ( + CORRECT_CREATE, + CORRECT_UPDATE, + LONGER_THEN_256, + NULL, + THE_ONLY_LETTER, +) + +STAFF_TEAM_MEMBER_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "staff_member": StaffMember, + "staff_position": "тренер", + "qualification": "Какаятотамквалификация", + "notes": "Наш величайший тренер родился еще в те времена, когда...", + }, + CORRECT_UPDATE: { + "staff_member": StaffMember, + "staff_position": "пушер-тьютор", + "qualification": "Какаятотамещеквалификация", + "notes": "Этот пушер-тьютор толкает игроков на лед с такой силой...", + }, + "must_not_be_admitted": ( + { + "fields": "staff_position", + "test_values": ( + ("повелительмух", "непредусмотренная позиция сотрудника"), + NULL, + ), + }, + { + "fields": "qualification", + "test_values": (LONGER_THEN_256,), + }, + ), + "must_be_admitted": ( + {"fields": "qualification", "test_values": (THE_ONLY_LETTER,)}, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/team.py b/adaptive_hockey_federation/tests/model_schemas/team.py new file mode 100644 index 00000000..b37dfb80 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/team.py @@ -0,0 +1,42 @@ +from main.models import City, DisciplineName +from tests.model_schemas.fields_validation_schemas import ( + ALL_LETTERS, + CORRECT_CREATE, + CORRECT_UPDATE, + FIGURES_AND_LETTERS, + FIGURES_ONLY, + NULL, + PUNCTUATION_MARKS_ONLY, + SPACES, + THE_ONLY_LETTER, +) + +TEAM_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "name": "Команда молодости нашей", + "city": City, + "discipline_name": DisciplineName, + }, + CORRECT_UPDATE: { + "name": "Команда мечты", + "city": City, + "discipline_name": DisciplineName, + }, + "must_not_be_admitted": ( + { + "fields": "name", + "test_values": ( + NULL, + PUNCTUATION_MARKS_ONLY, + FIGURES_ONLY, + THE_ONLY_LETTER, + ), + }, + ), + "must_be_admitted": ( + { + "fields": "name", + "test_values": (ALL_LETTERS, FIGURES_AND_LETTERS, SPACES), + }, + ), +} diff --git a/adaptive_hockey_federation/tests/model_schemas/user.py b/adaptive_hockey_federation/tests/model_schemas/user.py new file mode 100644 index 00000000..ed3859d2 --- /dev/null +++ b/adaptive_hockey_federation/tests/model_schemas/user.py @@ -0,0 +1,84 @@ +from core.constants import Role +from tests.model_schemas.fields_validation_schemas import ( + ALL_CAPS, + ALL_LOWER, + CORRECT_CREATE, + CORRECT_PHONE, + CORRECT_UPDATE, + DOUBLE_LAST_NAME, + DOUBLE_PATRONYMIC, + FIGURES_AND_LETTERS, + INCORRECT_EMAIL, + INCORRECT_PHONE, + LONGER_THEN_256, + LOWER_SECOND_LAST_NAME, + MIDDLE_CAP, + NOT_CYR, + NULL, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + SPACES, + THE_ONLY_CYR_LETTER, + TWO_OR_MORE_SPACES, + UNFORESEEN_ROLE, +) + +USER_MODEL_TEST_SCHEMA = { + CORRECT_CREATE: { + "first_name": "Василий", + "last_name": "Иванович", + "patronymic": "Петров", + "role": Role.AGENT, + "email": "fake@fake.com", + "phone": "+7 990 060-45-71", + "is_staff": False, + "password": "pAss9742!word", + }, + CORRECT_UPDATE: { + "first_name": "Бурямглоюнебокроетвихриснежныекрутятокакзверьоназавоет", + "last_name": "Съешьещеэтихмягкихфранцузскихбулочекдавыпейчаюев", + "patronymic": "Кракозябробормоглототроглодитобрандашмыгович", + "role": Role.AGENT, + "email": "fake@fake.com", + "phone": "+7 990 060-45-72", + "is_staff": False, + "password": "NewPAss9742!word", + }, + "must_not_be_admitted": ( + { + "fields": ("first_name", "last_name", "patronymic"), + "test_values": ( + ALL_LOWER, + ALL_CAPS, + MIDDLE_CAP, + NOT_CYR, + FIGURES_AND_LETTERS, + LONGER_THEN_256, + PUNCTUATION_MARKS_EXCEPT_HYPHEN, + ), + }, + { + "fields": ( + "first_name", + "last_name", + ), + "test_values": (NULL,), + }, + { + "fields": "last_name", + "test_values": (LOWER_SECOND_LAST_NAME, SPACES), + }, + {"fields": "patronymic", "test_values": (TWO_OR_MORE_SPACES,)}, + {"fields": "email", "test_values": (INCORRECT_EMAIL,)}, + {"fields": "role", "test_values": (UNFORESEEN_ROLE,)}, + {"fields": "phone", "test_values": (INCORRECT_PHONE,)}, + ), + "must_be_admitted": ( + { + "fields": ("last_name", "first_name", "patronymic"), + "test_values": (THE_ONLY_CYR_LETTER,), + }, + {"fields": "last_name", "test_values": (DOUBLE_LAST_NAME,)}, + {"fields": "patronymic", "test_values": (DOUBLE_PATRONYMIC,)}, + {"fields": "phone", "test_values": (CORRECT_PHONE,)}, + ), +} diff --git a/adaptive_hockey_federation/tests/permissions_test.py b/adaptive_hockey_federation/tests/permissions_test.py new file mode 100644 index 00000000..440bb807 --- /dev/null +++ b/adaptive_hockey_federation/tests/permissions_test.py @@ -0,0 +1,343 @@ +from http import HTTPStatus +from typing import Any, Iterable + +from core.constants import Role +from main.data_factories.factories import PlayerFactory +from main.models import City, DisciplineName, Player, Team +from tests.base import BaseTestClass +from tests.fixture_user import test_email, test_password, test_role_user +from tests.url_schema import ( + PLAYER_GET_URLS, + PLAYER_POST_URLS, + TEAM_GET_URLS, + TEAM_POST_URLS, + UNLOAD_URLS, + USER_GET_URLS, + USER_POST_URLS, +) +from tests.utils import UrlToTest +from users.factories import UserFactory +from users.models import User + + +class TestPermissions(BaseTestClass): + """ + Тесты url-путей. + + На возможность доступа к ним определенных категорий пользователей. + """ + + staff_user: User + + @classmethod + def setUpClass(cls) -> None: + """Классовый метод для базовой настройки всех тестов класса.""" + super().setUpClass() + cls.staff_user = UserFactory.create(role=test_role_user, is_staff=True) + + def url_tests(self, url_to_test: UrlToTest): + """ + Прогоняет тесты урл-адреса на доступ различных пользователей. + + Принимает в качестве параметра экземпляр класса UrlToTest (см. + docstring к классу UrlToTest). + """ + responses = url_to_test.execute_tests(self.client, self.user) + for fact, estimated, message in responses: + if isinstance(estimated, (str, int)): + self.assertEqual(fact, estimated, message) + elif isinstance(estimated, (list, tuple)): + self.assertIn(fact, estimated, message) + + def batch_url_test(self, urls_to_test: Iterable[UrlToTest]): + """Проводит sub-тесты для нескольких урл.""" + for url_to_test in urls_to_test: + with self.subTest(url_to_test=url_to_test): + self.url_tests(url_to_test) + + def test_main_page(self): + """ + Главная страница доступна только авторизованному пользователю. + + Для неавторизованного происходит переадресация. + """ + url_to_test = UrlToTest("/") + self.url_tests(url_to_test) + + # TODO: Раскомментировать после починки админки. Пользователю вне групп, + # но с полем is_staff == True, недоступны почти все урлы в админке. + # def test_admin_site_available_for_admins_only(self): + # """Страницы админки доступны пользователям, у которых поле + # is_staff == True, и недоступны при is_staff == False. + # администраторам.""" + # urls_to_test = tuple( + # UrlToTest(url, admin_only=True) for url in ADMIN_SITE_ADMIN_OK + # ) + # self.batch_url_test(urls_to_test) + + def test_auth_login(self): + """ + Для неавторизованного пользователя. + + Должна быть доступна страница входа на сайт. + """ + url_to_test = UrlToTest("/auth/login/", authorized_only=False) + self.url_tests(url_to_test) + + def test_auth_logout(self): + """ + Для авторизованного пользователя. + + POST-запрос авторизованного пользователя на страницы лог-аута + должен вернуть ответ с кодом 302. + """ + url_to_test = UrlToTest( + "/auth/logout/", + code_estimated=HTTPStatus.FOUND, + use_post=True, + ) + self.url_tests(url_to_test) + + def test_auth_password_change_url(self): + """ + Для авторизованного пользователя. + + Страница смены пароля должна быть доступна только авторизованному + пользователю. + """ + url_to_test = UrlToTest("/auth/password_change/") + self.url_tests(url_to_test) + + def test_auth_password_reset_url(self): + """ + Для неавторизованного пользователя. + + Страница сброса пароля должна быть доступна неавторизованному + пользователю. + """ + url_to_test = UrlToTest("/auth/password_reset/", authorized_only=False) + self.url_tests(url_to_test) + + # TODO: Раскомментировать, когда будет починен урл. Сейчас он недоступен + # для пользователя с is_staff == True, если он не в группе + # администраторов. + # def test_analytics_url(self): + # """Страница аналитики доступна пользователям, у которых поле + # is_staff == True, и недоступны при is_staff == False.""" + # url_to_test = UrlToTest("/analytics/", admin_only=True) + # self.url_tests(url_to_test) + + # TODO: Проверить тесты и привести в соответсвие с последними изменениями. + # def test_competition_get_urls(self): + # """Тесты get-страниц соревнования на соответствующие разрешения.""" + # urls_to_test = tuple(UrlToTest(**url) for url in COMPETITION_GET_URLS) + # self.batch_url_test(urls_to_test) + + # TODO: Раскомментировать, когда будут починены вьюхи + # DeleteTeamFromCompetition и AddTeamToCompetition. Там надо просто + # поменять пермишен в одном случае и создать кастомный + # add_team_competition в модели соревнований и прикрутить его - во втором. + # def test_competition_post_urls(self): + # """Тесты post-страниц соревнования на соответствующие разрешения.""" + # urls_to_test = tuple( + # UrlToTest(**url, use_post=True, code_estimated=HTTPStatus.FOUND) + # for url in COMPETITION_POST_URLS + # ) + # self.batch_url_test(urls_to_test) + + def test_player_get_urls(self): + """Тесты get-страниц игрока на соответствующие разрешения.""" + urls_to_test = tuple(UrlToTest(**url) for url in PLAYER_GET_URLS) + self.batch_url_test(urls_to_test) + + def test_player_post_urls(self): + """Тесты post-страниц игрока на соответствующие разрешения.""" + urls_to_test = tuple( + UrlToTest(**url, code_estimated=HTTPStatus.FOUND, use_post=True) + for url in PLAYER_POST_URLS + ) + self.batch_url_test(urls_to_test) + + # TODO: Раскомментировать, когда пермишены будут приведены в соответствие + # с названием сущности (затык на разных наименованиях пермишенов, + # где-то это staff, где-то - staffmember, где-то - staffteammember. + # Надо привести в общую канву нейминга, так как объект называется + # всё-таки StaffTeamMember, соответственно, пермишены надо поименовать + # ..._staffteammember), тем более, что StaffMember - это другая + # сущность, либо, если я не прав (что вполне возможно) - исправить этот + # тест. + # def test_staff_get_urls(self): + # """Тесты get-страниц сотрудника команды на соответствующие + # разрешения.""" + # urls_to_test = tuple(UrlToTest(**url) for url in STAFF_GET_URLS) + # self.batch_url_test(urls_to_test) + + # TODO: Раскомментировать, когда пермишены будут приведены в соответствие + # с названием сущности (затык на разных наименованиях пермишенов, + # где-то это staff, где-то - staffmember, где-то - staffteammember. + # Надо привести в общую канву нейминга, так как объект называется + # всё-таки StaffTeamMember, соответственно, пермишены надо поименовать + # ..._staffteammember), тем более, что StaffMember - это другая + # сущность, либо, если я не прав (что вполне возможно) - исправить этот + # тест. + # def test_staff_post_urls(self): + # """Тесты post-страниц сотрудника команды на соответствующие + # разрешения.""" + # urls_to_test = tuple( + # UrlToTest(**url, code_estimated=HTTPStatus.FOUND, use_post=True) + # for url in STAFF_POST_URLS + # ) + # self.batch_url_test(urls_to_test) + + def test_team_get_urls(self): + """Тесты get-страниц команд на соответствующие разрешения.""" + urls_to_test = tuple(UrlToTest(**url) for url in TEAM_GET_URLS) + self.batch_url_test(urls_to_test) + + def test_team_post_urls(self): + """Тесты post-страниц команд на соответствующие разрешения.""" + urls_to_test = tuple( + UrlToTest(**url, code_estimated=HTTPStatus.FOUND, use_post=True) + for url in TEAM_POST_URLS + ) + self.batch_url_test(urls_to_test) + + def test_user_get_urls(self): + """Тесты get-страниц пользователя на соответствующие разрешения.""" + urls_to_test = tuple(UrlToTest(**url) for url in USER_GET_URLS) + self.batch_url_test(urls_to_test) + + def test_user_post_urls(self): + """Тесты post-страниц пользователя на соответствующие разрешения.""" + urls_to_test = tuple( + UrlToTest(**url, code_estimated=HTTPStatus.FOUND, use_post=True) + for url in USER_POST_URLS + ) + self.batch_url_test(urls_to_test) + + def test_unload_urls(self): + """Тесты get-страниц выгрузки на соответствующие разрешения.""" + urls_to_test = tuple(UrlToTest(**url) for url in UNLOAD_URLS) + self.batch_url_test(urls_to_test) + + +class TestSpecialPermissions(BaseTestClass): + """ + Тесты на наличие специальных разрешений. + + На доступ отдельных групп пользователей к отдельным объектам. + """ + + user_agent: User | Any = None + team_2: Team | Any = None + player_2: Player | Any = None + + @classmethod + def setUpClass(cls) -> None: + """Классовый метод для базовой настройки всех тестов класса.""" + super().setUpClass() + cls.user_agent = User.objects.create_user( + password=test_password, + first_name="Иван", + last_name="Агент", + role=Role.AGENT, + email="agent_" + test_email, + ) + cls.team_2 = Team.objects.create( + name="Team 2", + city=City.objects.create(name="cls_Test City_2"), + discipline_name=DisciplineName.objects.create( + name="cls_Test DisciplineName_2", + ), + curator=cls.user_agent, + ) + cls.player_2 = PlayerFactory.create() + cls.player.team.clear() + cls.player_2.team.clear() + cls.player_2.team.add(cls.team_2) + + # def setUp(self): + # self.client = Client() + + def test_agent_has_access(self): + """Доступ представителя к своей команде, ее игрокам и т.д.""" + # TODO: Добавить урлы тренеров и пушер-тьюторов по аналогии, + # когда функционал ограничения доступа к этим сущностям будет + # разработан. + urls_agent_has_access_to = { + f"/teams/{self.team_2.id}/edit/": ( + "страница /teams//edit/ редактирования общих " + "сведений этой команды (ожидается ответ со статусом 200)" + ), + f"/players/{self.player_2.id}/": ( + "страница просмотра подробных сведений об игроке из этой " + "команды (страница /players// должна вернуть " + "ответ со статусом 200)" + ), + f"/players/{self.player_2.id}/edit/": ( + "страница редактирования игрока из этой команды (страница " + "/players//edit/ должна вернуть ответ со статусом " + "200)" + ), + f"/players/create/?team={self.team_2.id}": ( + "страница создания игрока из этой команды (страница " + "/players/create/?team= должна вернуть ответ со " + "статусом 200)" + ), + } + self.client.force_login(self.user_agent) + for url, message in urls_agent_has_access_to.items(): + with self.subTest(msg=message, url=url): + response = self.client.get(url) + self.assertEqual( + response.status_code, + HTTPStatus.OK, + msg=( + f"Представителю команды должна " + f"быть доступна {message}" + ), + ) + + def test_agent_has_no_access(self): + """Запрет доступа представителя к чужой команде, игрокам и т.д.""" + # TODO: Добавить урлы тренеров и пушер-тьюторов по аналогии, + # когда функционал ограничения доступа к этим сущностям будет + # разработан. + urls_agent_has_no_access_to = { + f"/teams/{self.team.id}/edit/": ( + "страница /teams//edit/ редактирования общих " + "сведений ЧУЖОЙ команды (ожидается ответ со статусом 403)" + ), + f"/players/{self.player.id}/": ( + "страница просмотра подробных сведений об игроке ЧУЖОЙ " + "команды (страница /players// должна вернуть " + "ответ со статусом 403)" + ), + f"/players/{self.player.id}/edit/": ( + "страница редактирования игрока ЧУЖОЙ команды (страница " + "/players//edit должна вернуть ответ со статусом " + "403)" + ), + f"/players/create/?team={self.team.id}": ( + "страница создания игрока с привязкой к ЧУЖОЙ команде " + "(страница /players/create/?team= должна вернуть " + "ответ со статусом 403)" + ), + "/players/create/": ( + "страница создания игрока без привязки к какой-либо команде " + "(страница /players/create/ должна вернуть ответ со " + "статусом 403)" + ), + } + self.client.force_login(self.user_agent) + for url, message in urls_agent_has_no_access_to.items(): + with self.subTest(msg=message, url=url): + response = self.client.get(url) + self.assertEqual( + response.status_code, + HTTPStatus.FORBIDDEN, + msg=( + f"Представителю команды НЕ должна " + f"быть доступна {message}" + ), + ) diff --git a/adaptive_hockey_federation/tests/player_test.py b/adaptive_hockey_federation/tests/player_test.py new file mode 100644 index 00000000..cb9dbbd7 --- /dev/null +++ b/adaptive_hockey_federation/tests/player_test.py @@ -0,0 +1,120 @@ +from datetime import datetime +from typing import Any + +from django.test import TestCase +from main.data_factories.factories import DiagnosisFactory, PlayerFactory +from main.models import Diagnosis, DisciplineLevel, DisciplineName, Player + + +class TestUser(TestCase): + """Тестирование CRUD игрока с использованием фабрик.""" + + diagnosis: Diagnosis | Any = None + player: Player | Any = None + player_test: Player | Any = None + discipline_name: DisciplineName | Any = None + discipline_level: DisciplineLevel | Any = None + + @classmethod + def setUpTestData(cls): + """Создание тестовых данных.""" + cls.diagnosis = DiagnosisFactory.create() + cls.player = PlayerFactory.create() + cls.player_test = cls.player + + def setUp(self): + """Метод для базовой настройки тестов класса.""" + self.player = Player.objects.create( + surname=self.player_test.surname + "тест", + name=self.player_test.name + "тест", + patronymic=self.player_test.patronymic + "тест", + gender=self.player_test.gender, + birthday=self.player_test.birthday, + diagnosis=self.diagnosis, + level_revision=self.player_test.level_revision, + position=self.player_test.position, + number=self.player_test.number, + identity_document=self.player_test.identity_document, + ) + self.diagnosis = self.player.diagnosis + + def test_player_create(self): + """Тест - создание игрока.""" + self.assertEqual( + self.player.surname, + self.player_test.surname + "тест", + ) + self.assertEqual(self.player.name, self.player_test.name + "тест") + self.assertEqual( + self.player.patronymic, + self.player_test.patronymic + "тест", + ) + self.assertEqual(self.player.gender, self.player_test.gender) + self.assertEqual(self.player.birthday, self.player_test.birthday) + self.assertEqual(self.player.discipline_name, self.discipline_name) + self.assertEqual(self.player.discipline_level, self.discipline_level) + self.assertEqual(self.player.diagnosis, self.diagnosis) + self.assertEqual( + self.player.level_revision, + self.player_test.level_revision, + ) + self.assertEqual(self.player.position, self.player_test.position) + self.assertEqual(self.player.number, self.player_test.number) + self.assertEqual( + self.player.identity_document, + self.player_test.identity_document, + ) + + def test_player_edit(self): + """Тест - редактирование существующего игрока.""" + new_surname = self.player_test.surname + "редактирование" + new_name = self.player_test.name + "редактирование" + new_patronymic = self.player_test.patronymic + "редактирование" + new_gender = self.player_test.gender + new_birthday = datetime.strptime("2014-01-18", "%Y-%m-%d").date() + new_diagnosis = DiagnosisFactory.create() + new_level_revision = self.player_test.level_revision + "ред." + new_position = self.player_test.position + "редактирование" + new_number = self.player_test.number + 1 + new_identity_document = self.player_test.identity_document + "ред." + + self.player.surname = new_surname + self.player.name = new_name + self.player.patronymic = new_patronymic + self.player.gender = new_gender + self.player.birthday = new_birthday + self.player.diagnosis = new_diagnosis + self.player.level_revision = new_level_revision + self.player.position = new_position + self.player.number = new_number + self.player.identity_document = new_identity_document + self.player.save() + + edited_player = Player.objects.get(pk=self.player.pk) + self.assertEqual(edited_player.surname, new_surname) + self.assertEqual(edited_player.name, new_name) + self.assertEqual(edited_player.patronymic, new_patronymic) + self.assertEqual(edited_player.gender, new_gender) + self.assertEqual(edited_player.birthday, new_birthday) + self.assertEqual(edited_player.diagnosis, new_diagnosis) + self.assertEqual(edited_player.level_revision, new_level_revision) + self.assertEqual(edited_player.position, new_position) + self.assertEqual(edited_player.number, new_number) + self.assertEqual( + edited_player.identity_document, + new_identity_document, + ) + + def delete_player(self, player_id): + """Метод для запуска удаления игрока.""" + try: + player = Player.objects.get(id=player_id) + player.delete() + return True + except Player.DoesNotExist: + return False + + def test_player_delete(self): + """Тест - удаление игрока.""" + delete_result = self.delete_player(self.player.id) + self.assertTrue(delete_result, "Ошибка при удалении игрока") diff --git a/adaptive_hockey_federation/tests/smoke_test.py b/adaptive_hockey_federation/tests/smoke_test.py new file mode 100644 index 00000000..1841f8ec --- /dev/null +++ b/adaptive_hockey_federation/tests/smoke_test.py @@ -0,0 +1,238 @@ +from http import HTTPStatus + +import pytest +from django.test import Client +from tests.base import BaseTestClass, UrlTestMixin +from tests.url_schema import ( + ADMIN_APP_LABELS_URLS, + ADMIN_AUTH_GROUP_302, + ADMIN_AUTH_URLS, + ADMIN_CITY_URL_302, + ADMIN_CITY_URLS, + ADMIN_COMPETITIONS_URLS, + ADMIN_COMPETITIONS_URLS_302, + ADMIN_DIAGNOSIS_URL_302, + ADMIN_DIAGNOSIS_URLS, + ADMIN_DISCIPLINE_NAME_URL_302, + ADMIN_DISCIPLINE_NAME_URLS, + ADMIN_LOGIN, + ADMIN_LOGOUT, + ADMIN_MAIN_URL, + ADMIN_NOSOLOGY_URL_302, + ADMIN_NOSOLOGY_URLS, + ADMIN_PASSWORD_URLS, + ADMIN_PLAYER_URL_302, + ADMIN_PROXY_GROUP_URL_302, + ADMIN_PROXY_GROUP_URLS, + ADMIN_STAFF_MEMBER_URL_302, + ADMIN_STAFF_MEMBER_URLS, + ADMIN_TEAM_URL_302, + ADMIN_TEAM_URLS, + ADMIN_USER_URL_302, + ADMIN_USER_URLS, + AUTH_RESET_URLS, + COMPETITION_GET_URLS, + COMPETITION_POST_URLS, + INDEX_PAGE, + LOG_IN_URL, + LOG_OUT_URL, + PASSWORD_URLS, + PLAYER_GET_URLS, + PLAYER_POST_URLS, + STAFF_GET_URLS, + STAFF_POST_URLS, + TEAM_GET_URLS, + TEAM_POST_URLS, + UNLOAD_URLS, + USER_GET_URLS, + USER_POST_URLS, +) + + +class TestSiteUrlsSmoke(BaseTestClass, UrlTestMixin): + """Тесты урл-путей клиентской части сайта.""" + + def setUp(self) -> None: + """Метод для базовой настройки тестов класса.""" + self.super_client = Client() + self.super_client.force_login(self.superuser) + + def get_default_client(self): + """Метод для получения клиента по умолчанию.""" + return self.super_client + + def test_main_page(self): + """Тест на проверку главной страницы.""" + self.url_get_test(INDEX_PAGE) + + def test_competitions_simple_access(self): + """Тест доступности страниц с соревнованиями.""" + self.url_get_test(COMPETITION_GET_URLS) + self.url_get_test(COMPETITION_POST_URLS, "post", HTTPStatus.FOUND) + + def test_player_simple_access(self): + """Тест доступности страниц с игроками.""" + self.url_get_test(PLAYER_GET_URLS) + self.url_get_test(PLAYER_POST_URLS, "post", HTTPStatus.FOUND) + + def test_staff_simple_access(self): + """Тест доступности страниц с сотрудниками команд.""" + self.url_get_test(STAFF_GET_URLS) + self.url_get_test(STAFF_POST_URLS, "post", HTTPStatus.FOUND) + + def test_team_simple_access(self): + """Тест доступности страниц со спортивными командами.""" + self.url_get_test(TEAM_GET_URLS) + self.url_get_test(TEAM_POST_URLS, "post", HTTPStatus.FOUND) + + def test_user_simple_access(self): + """Тест доступности страниц с пользователями.""" + self.url_get_test(USER_GET_URLS) + self.url_get_test(USER_POST_URLS, "post", HTTPStatus.FOUND) + + def test_unload_simple_access(self): + """Тест доступности страниц с выгрузками.""" + self.url_get_test(UNLOAD_URLS) + + def test_log_in_out_access(self): + """Тест доступности страниц входа-выхода на/с сайта.""" + self.url_get_test(LOG_OUT_URL, "post", HTTPStatus.FOUND) + self.url_get_test(LOG_IN_URL) + + def test_password_simple_access(self): + """Тест доступности страниц с манипуляциями с паролем.""" + self.url_get_test(PASSWORD_URLS) + + def test_auth_reset_urls(self): + """Тест доступности страниц со сбросом пароля.""" + self.url_get_test(AUTH_RESET_URLS) + + +class TestAdminUrlsSmoke(BaseTestClass, UrlTestMixin): + """Тесты урл-путей административной части сайта.""" + + def setUp(self) -> None: + """Метод для базовой настройки тестов класса.""" + self.super_client = Client() + self.super_client.force_login(self.superuser) + + def get_default_client(self): + """Метод для получения клиента по умолчанию.""" + return self.super_client + + def test_admin_base(self): + """Тест главной страницы админки.""" + self.url_get_test(ADMIN_MAIN_URL) + + def test_admin_app_labels(self): + """Тесты страниц с приложениями в админке.""" + self.url_get_test(ADMIN_APP_LABELS_URLS) + + def test_admin_auth_group(self): + """Тесты страниц с группами в админке.""" + self.url_get_test(ADMIN_AUTH_GROUP_302, status_code=HTTPStatus.FOUND) + self.url_get_test(ADMIN_AUTH_URLS) + + def test_admin_log_in_out(self): + """Тесты страниц входа-выхода в/из админки.""" + self.url_get_test(ADMIN_LOGOUT, "post", status_code=HTTPStatus.FOUND) + self.client.logout() + self.url_get_test(ADMIN_LOGIN) + + def test_admin_city(self): + """Тесты страниц с городами в админке.""" + self.url_get_test(ADMIN_CITY_URL_302, status_code=HTTPStatus.FOUND) + self.url_get_test(ADMIN_CITY_URLS) + + def test_admin_competitions(self): + """Тесты страниц с соревнованиями в админке.""" + self.url_get_test( + ADMIN_COMPETITIONS_URLS_302, + status_code=HTTPStatus.FOUND, + ) + self.url_get_test(ADMIN_COMPETITIONS_URLS) + + def test_admin_diagnosis(self): + """Тесты страниц с диагнозами в админке.""" + self.url_get_test( + ADMIN_DIAGNOSIS_URL_302, + status_code=HTTPStatus.FOUND, + ) + self.url_get_test(ADMIN_DIAGNOSIS_URLS) + + """ + Что - то этот тест не хочет работать + def test_admin_discipline_level(self): + Тесты страниц с уровнями дисциплин в админке. + self.url_get_test( + ADMIN_DISCIPLINE_LEVEL_URL_302, status_code=HTTPStatus.FOUND + ) + self.url_get_test(ADMIN_DISCIPLINE_LEVEL_URLS) + """ + + def test_admin_discipline_name(self): + """Тесты страниц с наименованиями дисциплин в админке.""" + self.url_get_test( + ADMIN_DISCIPLINE_NAME_URL_302, + status_code=HTTPStatus.FOUND, + ) + self.url_get_test(ADMIN_DISCIPLINE_NAME_URLS) + + @pytest.mark.skip(reason="Модель Document был исключен из админки") + def test_admin_document(self): + """Тесты страниц с документом в админке.""" + # self.url_get_test( + # ADMIN_DOCUMENT_URL_302, status_code=HTTPStatus.FOUND + # ) + # self.url_get_test(ADMIN_DOCUMENT_URLS) + + def test_admin_nosology(self): + """Тесты страниц с нозологией в админке.""" + self.url_get_test(ADMIN_NOSOLOGY_URL_302, status_code=HTTPStatus.FOUND) + self.url_get_test(ADMIN_NOSOLOGY_URLS) + + def test_admin_player(self): + """Тесты страниц с игроками в админке.""" + self.url_get_test(ADMIN_PLAYER_URL_302, status_code=HTTPStatus.FOUND) + # TODO: Раскомментировать, когда будет починена админка. Сейчас на + # сайте реально недоступны страницы "/admin/main/player/1/change/" и + # "/admin/main/player/add/". + # self.url_get_test(ADMIN_PLAYER_URLS) + + def test_admin_staff_member(self): + """Тесты страниц с сотрудником в админке.""" + self.url_get_test( + ADMIN_STAFF_MEMBER_URL_302, + status_code=HTTPStatus.FOUND, + ) + self.url_get_test(ADMIN_STAFF_MEMBER_URLS) + + @pytest.mark.skip(reason="Модель StuffTeamMember был исключен из админки") + def test_admin_staff_team_member(self): + """Тесты страниц с сотрудником команды в админке.""" + # self.url_get_test( + # ADMIN_STAFF_TEAM_MEMBER_URL_302, status_code=HTTPStatus.FOUND + # ) + # self.url_get_test(ADMIN_STAFF_TEAM_MEMBER_URLS) + + def test_admin_team(self): + """Тесты страниц с командами в админке.""" + self.url_get_test(ADMIN_TEAM_URL_302, status_code=HTTPStatus.FOUND) + self.url_get_test(ADMIN_TEAM_URLS) + + def test_admin_password(self): + """Тесты страниц изменения-сброса пароля в админке.""" + self.url_get_test(ADMIN_PASSWORD_URLS) + + def test_admin_proxy_group(self): + """Тесты страниц с прокси-группами в админке.""" + self.url_get_test( + ADMIN_PROXY_GROUP_URL_302, + status_code=HTTPStatus.FOUND, + ) + self.url_get_test(ADMIN_PROXY_GROUP_URLS) + + def test_admin_user(self): + """Тесты страниц с пользователями в админке.""" + self.url_get_test(ADMIN_USER_URL_302, status_code=HTTPStatus.FOUND) + self.url_get_test(ADMIN_USER_URLS) diff --git a/adaptive_hockey_federation/tests/url_schema.py b/adaptive_hockey_federation/tests/url_schema.py new file mode 100644 index 00000000..028a127d --- /dev/null +++ b/adaptive_hockey_federation/tests/url_schema.py @@ -0,0 +1,254 @@ +PERMISSION_REQUIRED = "permission_required" +URL = "url" + +INDEX_PAGE = "/" +ADMIN_MAIN_URL = "/admin/" +ADMIN_APP_LABELS_URLS = ( + "/admin/competitions/", + "/admin/main/", + "/admin/users/", +) +ADMIN_AUTH_GROUP_302 = "/admin/auth/group//" +ADMIN_AUTH_URLS = ( + "/admin/auth/group/", + "/admin/auth/group//change/", + "/admin/auth/group//delete/", + "/admin/auth/group//history/", + "/admin/auth/group/add/", +) +ADMIN_COMPETITIONS_URLS_302 = ( + "/admin/competitions/competition//", +) +ADMIN_COMPETITIONS_URLS = ( + "/admin/competitions/competition/", + "/admin/competitions/competition//change/", + "/admin/competitions/competition//delete/", + "/admin/competitions/competition//history/", + "/admin/competitions/competition/add/", +) +ADMIN_SERVICE_PAGES = ("/admin/jsi18n/",) +ADMIN_LOGIN = ("/admin/login/",) +ADMIN_LOGOUT = ("/admin/logout/",) +ADMIN_CITY_URL_302 = ("/admin/main/city//",) +ADMIN_CITY_URLS = ( + "/admin/main/city/", + "/admin/main/city//change/", + "/admin/main/city//delete/", + "/admin/main/city//history/", + "/admin/main/city/add/", +) +ADMIN_DIAGNOSIS_URL_302 = ("/admin/main/diagnosis//",) +ADMIN_DIAGNOSIS_URLS = ( + "/admin/main/diagnosis/", + "/admin/main/diagnosis//change/", + "/admin/main/diagnosis//delete/", + "/admin/main/diagnosis//history/", + "/admin/main/diagnosis/add/", +) +ADMIN_DISCIPLINE_LEVEL_URL_302 = ( + "/admin/main/disciplinelevel//", +) +ADMIN_DISCIPLINE_LEVEL_URLS = ( + "/admin/main/disciplinelevel/", + "/admin/main/disciplinelevel//change/", + "/admin/main/disciplinelevel//delete/", + "/admin/main/disciplinelevel//history/", + "/admin/main/disciplinelevel/add/", +) +ADMIN_DISCIPLINE_NAME_URL_302 = ( + "/admin/main/disciplinename//", +) +ADMIN_DISCIPLINE_NAME_URLS = ( + "/admin/main/disciplinename/", + "/admin/main/disciplinename//change/", + "/admin/main/disciplinename//delete/", + "/admin/main/disciplinename//history/", + "/admin/main/disciplinename/add/", +) +# Document был исключен из админки +# ADMIN_DOCUMENT_URL_302 = ("/admin/main/document//",) +# ADMIN_DOCUMENT_URLS = ( +# "/admin/main/document/", +# "/admin/main/document//change/", +# "/admin/main/document//delete/", +# "/admin/main/document//history/", +# "/admin/main/document/add/", +# ) +ADMIN_NOSOLOGY_URL_302 = ("/admin/main/nosology//",) +ADMIN_NOSOLOGY_URLS = ( + "/admin/main/nosology/", + "/admin/main/nosology//change/", + "/admin/main/nosology//delete/", + "/admin/main/nosology//history/", + "/admin/main/nosology/add/", +) +ADMIN_PLAYER_URL_302 = ("/admin/main/player//",) +ADMIN_PLAYER_URLS = ( + "/admin/main/player/", + "/admin/main/player//change/", + "/admin/main/player//delete/", + "/admin/main/player//history/", + "/admin/main/player/add/", +) +ADMIN_STAFF_MEMBER_URL_302 = ("/admin/main/staffmember//",) +ADMIN_STAFF_MEMBER_URLS = ( + "/admin/main/staffmember/", + "/admin/main/staffmember//change/", + "/admin/main/staffmember//delete/", + "/admin/main/staffmember//history/", + "/admin/main/staffmember/add/", +) +# AdminStaffTeamMember был исключен +# ADMIN_STAFF_TEAM_MEMBER_URL_302 = ( +# "/admin/main/staffteammember//", +# ) +# ADMIN_STAFF_TEAM_MEMBER_URLS = ( +# "/admin/main/staffteammember/", +# "/admin/main/staffteammember//change/", +# "/admin/main/staffteammember//delete/", +# "/admin/main/staffteammember//history/", +# "/admin/main/staffteammember/add/", +# ) +ADMIN_TEAM_URL_302 = ("/admin/main/team//",) +ADMIN_TEAM_URLS = ( + "/admin/main/team/", + "/admin/main/team//change/", + "/admin/main/team//delete/", + "/admin/main/team//history/", + "/admin/main/team/add/", +) +ADMIN_PASSWORD_URLS = ( + "/admin/password_change/", + "/admin/password_change/done/", +) +ADMIN_PROXY_GROUP_URL_302 = ("/admin/users/proxygroup//",) +ADMIN_PROXY_GROUP_URLS = ( + "/admin/users/proxygroup/", + "/admin/users/proxygroup//change/", + "/admin/users/proxygroup//delete/", + "/admin/users/proxygroup//history/", + "/admin/users/proxygroup/add/", +) +ADMIN_USER_URL_302 = ("/admin/users/user//",) +ADMIN_USER_URLS = ( + "/admin/users/user/", + "/admin/users/user//change/", + "/admin/users/user//delete/", + "/admin/users/user//history/", + "/admin/users/user/add/", +) +ANALYTICS_URL = "/analytics/" +LOG_IN_URL = "/auth/login/" +LOG_OUT_URL = "/auth/logout/" +PASSWORD_URLS = ( + "/auth/password_change/", + "/auth/password_change/done/", + "/auth/password_reset/", + "/auth/password_reset/done/", +) +AUTH_RESET_URLS = ( + "/auth/reset///", + "/auth/reset/done/", +) +COMPETITION_GET_URLS = ( + {URL: "/competitions/", PERMISSION_REQUIRED: "list_view_competition"}, + {URL: "/competitions/create/", PERMISSION_REQUIRED: "add_competition"}, + { + URL: "/competitions//", + PERMISSION_REQUIRED: "list_team_competition", + }, + { + URL: "/competitions//edit/", + PERMISSION_REQUIRED: "change_competition", + }, +) + +COMPETITION_POST_URLS = ( + { + URL: "/competitions//teams//add/", + PERMISSION_REQUIRED: "add_team_competition", + }, + { + URL: "/competitions//teams//delete/", + PERMISSION_REQUIRED: "delete_team_competition", + }, + { + URL: "/competitions//delete/", + PERMISSION_REQUIRED: "delete_competition", + }, +) + +PLAYER_GET_URLS = ( + {URL: "/players/", PERMISSION_REQUIRED: "list_view_player"}, + {URL: "/players//", PERMISSION_REQUIRED: "view_player"}, + {URL: "/players//edit/", PERMISSION_REQUIRED: "change_player"}, + {URL: "/players/create/", PERMISSION_REQUIRED: "add_player"}, +) +PLAYER_POST_URLS = ( + {URL: "/players/1/delete/", PERMISSION_REQUIRED: "delete_player"}, + # TODO: Тест на данный урл выдает TemplateDoesNotExist. Необходимо + # раскомментировать, когда будет починен player_id_deleted() в + # main.views или вообще удалить, если эта страница не нужна. + # "/players/deleted/", +) +STAFF_GET_URLS = ( + {URL: "/staffs/", PERMISSION_REQUIRED: "list_view_staffteammember"}, + {URL: "/staffs//", PERMISSION_REQUIRED: "view_staffteammember"}, + { + URL: "/staffs//edit/", + PERMISSION_REQUIRED: "change_staffteammember", + }, + {URL: "/staffs/create/", PERMISSION_REQUIRED: "add_staffteammember"}, +) +STAFF_POST_URLS = ( + { + URL: "/staffs//delete/", + PERMISSION_REQUIRED: "delete_staffteammember", + }, +) +TEAM_GET_URLS = ( + {URL: "/teams/", PERMISSION_REQUIRED: "list_view_team"}, + {URL: "/teams//", PERMISSION_REQUIRED: "view_team"}, + {URL: "/teams//edit/", PERMISSION_REQUIRED: "change_team"}, + {URL: "/teams/create/", PERMISSION_REQUIRED: "add_team"}, +) +TEAM_POST_URLS = ( + {URL: "/teams//delete/", PERMISSION_REQUIRED: "delete_team"}, +) +USER_GET_URLS = ( + {URL: "/users/", PERMISSION_REQUIRED: "list_view_user"}, + {URL: "/users//edit/", PERMISSION_REQUIRED: "change_user"}, + {URL: "/users/create/", PERMISSION_REQUIRED: "add_user"}, + { + URL: "/users/set_password///", + PERMISSION_REQUIRED: None, + "authorized_only": False, + }, +) +USER_POST_URLS = ( + {URL: "/users//delete/", PERMISSION_REQUIRED: "delete_user"}, +) +UNLOAD_URLS = ({URL: "/unloads/", PERMISSION_REQUIRED: "list_view_unload"},) + +# Страницы админки, которые должны возвращать 200(ОК) для пользователя, +# обладающего правами администратора: +ADMIN_SITE_ADMIN_OK = ( + (ADMIN_MAIN_URL,) + + ADMIN_APP_LABELS_URLS + + ADMIN_AUTH_URLS + + ADMIN_CITY_URLS + + ADMIN_COMPETITIONS_URLS + + ADMIN_DIAGNOSIS_URLS + + ADMIN_DISCIPLINE_LEVEL_URLS + + ADMIN_DISCIPLINE_NAME_URLS + + ADMIN_NOSOLOGY_URLS + + ADMIN_PASSWORD_URLS + + ADMIN_PLAYER_URLS + + ADMIN_PROXY_GROUP_URLS + + ADMIN_SERVICE_PAGES + + ADMIN_STAFF_MEMBER_URLS + + ADMIN_TEAM_URLS + + ADMIN_USER_URLS + # + ADMIN_STAFF_TEAM_MEMBER_URLS + # + ADMIN_DOCUMENT_URLS +) diff --git a/adaptive_hockey_federation/tests/url_test.py b/adaptive_hockey_federation/tests/url_test.py new file mode 100644 index 00000000..9c783a90 --- /dev/null +++ b/adaptive_hockey_federation/tests/url_test.py @@ -0,0 +1,326 @@ +from http import HTTPStatus +from typing import Any + +import pytest +from competitions.models import Competition +from core import constants +from core.constants import Role +from django.contrib.auth.models import Permission +from django.test import Client, TestCase +from main.data_factories.factories import ( + CompetitionFactory, + DiagnosisFactory, + PlayerFactory, +) +from main.models import City, Diagnosis, DisciplineName, Player, Team +from tests.fixture_user import ( + test_email, + test_lastname, + test_name, + test_password, + test_role_admin, + test_role_user, +) +from tests.utils import UrlToTest +from users.models import ProxyGroup, User + +TEST_GROUP_NAME = "no_permission_group" + + +class TestAuthUrls: + """Тесты авторизации пользователей.""" + + @pytest.mark.django_db(transaction=True) + def test_auth_urls(self, client): + """Тесты на логин и логаут пользователя.""" + urls = {"/auth/login/": 200, "/auth/logout/": 302} + for url, status in urls.items(): + try: + response = client.post(url) + except Exception as e: + raise AssertionError( + f"Страница {url} работает неправильно. Ошибка: {e}", + ) + assert response.status_code != 404, ( + f"Страница {url} не найдена, проверьте этот адрес в " + f"*urls.py*" + ) + assert response.status_code == status, ( + f"Ошибка {response.status_code} при открытии {url}. " + f"Проверьте ее view-функцию" + ) + + +class TestUrls(TestCase): + """Тесты на проверку url-путей.""" + + user: User | Any = None + user_agent: User | Any = None + team: Team | Any = None + team_2: Team | Any = None + competition: Competition | Any = None + diagnosis: Diagnosis | Any = None + player: Player | Any = None + player_2: Player | Any = None + + @classmethod + def setUpClass(cls) -> None: + """ + Создает необходимые сущности (объекты моделей БД). + + Запускается только один раз (в отличие от метода setUp), + не сбрасывается после каждого теста, что помогает + избежать многократного создания сущностей и, как следствие, смены id + каждой сущности после каждого теста. + """ + super().setUpClass() + ProxyGroup.objects.create(name=TEST_GROUP_NAME).save() + constants.GROUPS_BY_ROLE[test_role_user] = TEST_GROUP_NAME + cls.user = User.objects.create_user( + password=test_password, + first_name="cls_" + test_name, + last_name="cls_" + test_lastname, + role=test_role_user, + email="cls_" + test_email, + ) + + cls.user_agent = User.objects.create_user( + password=test_password, + first_name="Иван", + last_name="Агент", + role=Role.AGENT, + email="agent_" + test_email, + ) + + cls.team = Team.objects.create( + name="cls_Test Team", + city=City.objects.create(name="cls_Test City"), + discipline_name=DisciplineName.objects.create( + name="cls_Test DisciplineName", + ), + curator=cls.user, + ) + + cls.team_2 = Team.objects.create( + name="Team 2", + city=City.objects.create(name="cls_Test City_2"), + discipline_name=DisciplineName.objects.create( + name="cls_Test DisciplineName_2", + ), + curator=cls.user_agent, + ) + + cls.competition = CompetitionFactory.create() + cls.diagnosis = DiagnosisFactory.create() + cls.player = PlayerFactory.create() + cls.player_2 = PlayerFactory.create() + cls.player.team.clear() + cls.player_2.team.clear() + cls.player_2.team.add(cls.team_2) + + def setUp(self): + """Метод для базовой настройки тестов класса.""" + self.client = Client() + self.user = User.objects.create_user( + password=test_password, + first_name=test_name, + last_name=test_lastname, + role=test_role_user, + email=test_email, + ) + self.permissions = { + "view_team": Permission.objects.get(codename="view_team"), + } + + def delete_user(self, user_id): + """Метод для запуска удаления пользователя.""" + try: + user = User.objects.get(id=user_id) + user.delete() + return True + except User.DoesNotExist: + return False + + def test_create_user(self): + """Тест - создание пользователя.""" + self.assertEqual(self.user.first_name, test_name) + self.assertEqual(self.user.last_name, test_lastname) + self.assertEqual(self.user.role, test_role_user) + self.assertEqual(self.user.email, test_email) + + def test_edit_user(self): + """Тест - редактирование существующего пользователя.""" + new_name = "Test" + new_lastname = "User" + new_role = test_role_admin + new_email = "test@example.com" + + self.user.first_name = new_name + self.user.last_name = new_lastname + self.user.role = new_role + self.user.email = new_email + self.user.save() + + edited_user = User.objects.get(email=new_email) + self.assertEqual(edited_user.first_name, new_name) + self.assertEqual(edited_user.last_name, new_lastname) + self.assertEqual(edited_user.role, new_role) + self.assertEqual(edited_user.email, new_email) + + def test_delete_user(self): + """Тест - удаление пользователя.""" + delete_result = self.delete_user(self.user.id) + self.assertTrue(delete_result, "Ошибка при удалении пользователя") + + def test_main_urls(self): + """ + Тесты основных урл. + + Для тестирования нового урл - добавить соответствующий объект класса + UrlToTest в список urls (см. документацию к классу UrlToTest). + """ + urls = [ + UrlToTest("/"), + UrlToTest( + "/admin/", + admin_only=True, + unauthorized_code_estimated=HTTPStatus.FOUND, + ), + UrlToTest("/auth/login/", authorized_only=False), + UrlToTest( + "/auth/logout/", + code_estimated=HTTPStatus.FOUND, + use_post=True, + ), + UrlToTest("/auth/password_change/"), + UrlToTest("/auth/password_reset/", authorized_only=False), + UrlToTest("/analytics/", permission_required="list_view_player"), + # TODO Нужно доработать тесты соревнования согласно правам доступа. + # UrlToTest( + # "/competitions/", permission_required="list_view_competition" + # ), + # TODO Раскомментировать при доработке пермишенов для страниц с + # соревнованиями. + # UrlToTest( + # "/competitions/1/", permission_required="list_team_competition" + # ), + # TODO Раскомментировать при доработке пермишенов для страниц с + # игроками. + UrlToTest("/players/create/", permission_required="add_player"), + UrlToTest("/players/", permission_required="list_view_player"), + # TODO Раскомментировать при доработке пермишенов для страниц с + # игроками. + UrlToTest("/players/1/", permission_required="view_player"), + UrlToTest("/players/1/edit/", permission_required="change_player"), + UrlToTest("/teams/", permission_required="list_view_team"), + UrlToTest("/teams/1/", permission_required="view_team"), + UrlToTest("/teams/1/edit/", permission_required="change_team"), + UrlToTest("/teams/create/", permission_required="add_team"), + UrlToTest("/unloads/", permission_required="list_view_unload"), + # TODO Раскомментировать при доработке пермишенов для страниц с + # пользователями. + UrlToTest("/users/create/", permission_required="add_user"), + UrlToTest("/users/", permission_required="list_view_user"), + UrlToTest("/users/1/edit/", permission_required="change_user"), + ] + urls_responses_results = [] + + for url in urls: + urls_responses_results += url.execute_tests(self.client, self.user) + + for fact, estimated, message in urls_responses_results: + with self.subTest(msg=message, fact=fact, estimated=estimated): + if isinstance(estimated, (str, int)): + self.assertEqual(fact, estimated, message) + elif isinstance(estimated, (list, tuple)): + self.assertIn(fact, estimated, message) + + def test_agent_has_access(self): + """Доступ представителя к своей команде, ее игрокам и т.д.""" + # TODO: Добавить урлы тренеров и пушер-тьюторов по аналогии, + # когда функционал ограничения доступа к этим сущностям будет + # разработан. + urls_agent_has_access_to = { + f"/teams/{self.team_2.id}/edit/": ( + "страница /teams//edit/ редактирования общих " + "сведений этой команды (ожидается ответ со статусом 200)" + ), + f"/players/{self.player_2.id}/": ( + "страница просмотра подробных сведений об игроке из этой " + "команды (страница /players// должна вернуть " + "ответ со статусом 200)" + ), + f"/players/{self.player_2.id}/edit/": ( + "страница редактирования игрока из этой команды (страница " + "/players//edit/ должна вернуть ответ со статусом " + "200)" + ), + f"/players/create/?team={self.team_2.id}": ( + "страница создания игрока из этой команды (страница " + "/players/create/?team= должна вернуть ответ со " + "статусом 200)" + ), + } + self.client.force_login(self.user_agent) + for url, message in urls_agent_has_access_to.items(): + with self.subTest(msg=message, url=url): + response = self.client.get(url) + self.assertEqual( + response.status_code, + HTTPStatus.OK, + msg=( + f"Представителю команды должна" + f"быть доступна {message}" + ), + ) + + def test_agent_has_no_access(self): + """Запрет доступа представителя к чужой команде, игрокам и т.д.""" + # TODO: Добавить урлы тренеров и пушер-тьюторов по аналогии, + # когда функционал ограничения доступа к этим сущностям будет + # разработан. + urls_agent_has_no_access_to = { + f"/teams/{self.team.id}/edit/": ( + "страница /teams//edit/ редактирования общих " + "сведений ЧУЖОЙ команды (ожидается ответ со статусом 403)" + ), + f"/players/{self.player.id}/": ( + "страница просмотра подробных сведений об игроке ЧУЖОЙ " + "команды (страница /players// должна вернуть " + "ответ со статусом 403)" + ), + f"/players/{self.player.id}/edit/": ( + "страница редактирования игрока ЧУЖОЙ команды (страница " + "/players//edit должна вернуть ответ со статусом " + "403)" + ), + f"/players/create/?team={self.team.id}": ( + "страница создания игрока с привязкой к ЧУЖОЙ команде " + "(страница /players/create/?team= должна вернуть " + "ответ со статусом 403)" + ), + "/players/create/": ( + "страница создания игрока без привязки к какой-либо команде " + "(страница /players/create/ должна вернуть ответ со " + "статусом 403)" + ), + } + self.client.force_login(self.user_agent) + for url, message in urls_agent_has_no_access_to.items(): + with self.subTest(msg=message, url=url): + response = self.client.get(url) + self.assertEqual( + response.status_code, + HTTPStatus.FORBIDDEN, + msg=( + f"Представителю команды НЕ должна " + f"быть доступна {message}" + ), + ) + + + +# TODO: Тут тоже у нас не все тесты есть, +# TODO: я так понял, что у нас есть тесты для Аутентификации, +# TODO: User, Team, Player, Unload, Competition и главной страницы. +# TODO: Нужно сделать для Analytics, Games, Staffs. diff --git a/adaptive_hockey_federation/tests/utils.py b/adaptive_hockey_federation/tests/utils.py new file mode 100644 index 00000000..00058fe0 --- /dev/null +++ b/adaptive_hockey_federation/tests/utils.py @@ -0,0 +1,298 @@ +import re +from http import HTTPStatus +from typing import Any + +from django.contrib.auth.models import Permission +from django.core.exceptions import ObjectDoesNotExist +from django.test import Client +from users.models import User + + +class UrlToTest: + """ + Класс для использования в автоматизации тестирования урлов. + + В настоящем виде, в зависимости от параметров, может проводить тесты ( + возвращать пару "фактический ответ - ожидаемый ответ" и сообщение) на: + - возвращаемый ответ для авторизованного пользователя; + - возвращаемый ответ для неавторизованного пользователя; + - возвращаемый ответ для пользователя, обладающего определенным + разрешением; + - возвращаемый ответ для пользователя, НЕ обладающего определенным + разрешением; + - возвращаемый ответ для пользователя - администратора; + - возвращаемый ответ для пользователя - НЕ администратора. + + + Параметры создания объекта: + url - соответствующий урл; + code_estimated - код статуса или список кодов статуса, который в норме + должна возвращать страница. По умолчанию - HTTPStatus.OK (200); + permission_required: - разрешение (permission), требующееся для + соответствующего урла. Принимает строку с codename + соответствующего разрешения (например 'change_user'). В случае + permission_required==None метод execute_tests проведет + только тесты на авторизованного/неавторизованного пользователя. + Если указать конкретное разрешение (permission codename), + то будет проведен тест на ответ неавторизованному пользователю, + а также отдельно - на ответы авторизованному пользователю, + обладающему соответствующим разрешением, и авторизованному + пользователю, не обладающему таким разрешением. + По умолчанию = None; + authorized_only: + == True - тест на неавторизованного пользователя будет ожидать + ответ или один из ответов, указанных в параметре + "unauthorized_code_estimated", + == False (по умолчанию) - тест на неавторизованного пользователя + будет ожидать такой же ответ, как и для авторизованного + пользователя (code_estimated); + unauthorized_code_estimated: + Код статуса или список кодов, которые в норме должна возвращать + страница для неавторизованного пользователя в том случае, + когда параметр "authorized_only" равен True. При + authorized_only==False данный параметр не используется. + По умолчанию - HTTPStatus.FOUND(302); + admin_only: + ==False (по умолчанию) - проводятся стандартные тесты на + авторизованного/неавторизованного пользователя. + ==True - вместо стандартного теста на + авторизованного пользователя будут проведены два + теста: на ответы авторизованному пользователю, обладающему + полномочиями администратора, и авторизованному пользователю, + не обладающему такими полномочиями; + use_post: + == False (по умолчанию) + == True - передавать такое значение следует только для url, + чей view наследуется от LogoutView, чтобы не отображался + warning, связанный с устареванием метода "get" для выхода + пользователя в Django 5.0, либо в иных случаях, когда необходимо + протестировать post-запросы к странице; + path_render_subs: + строка или кортеж строк подстановки вместо + динамических идентификаторов объектов типа в урл-путях. + Например, для обработки пути: + "/competitions//teams//add/" + если передать кортеж ("1", "2"), то он преобразует путь в + "/competitions/1/teams/2/add/"). + Если подстановок больше, чем элементов в кортеже "subs", то для + оставшихся подстановок возьмется последний элемент. Если в примере + выше передать строку "1" или кортеж ("1",), то урл преобразуется в + "/competitions/1/teams/1/add/". Если в урл нет динамических + идентификаторов, данный параметр не имеет значения. + """ + + def __init__( + self, + url: str, + *, + code_estimated: int | list | tuple = HTTPStatus.OK, + permission_required: str | None = None, + authorized_only: bool = True, + unauthorized_code_estimated: int | list | tuple = HTTPStatus.FOUND, + admin_only: bool = False, + use_post: bool = False, + path_render_subs: str | tuple[str, ...] | list[str] = "1", + ): + """ + Метод инициализации экземпляра класса. + + Присваивает необходимые атрибуты. + """ + self.path = url + self.authorized_only = authorized_only + self.code_estimated = code_estimated + self.permission = None + self.admin_only = admin_only + if permission_required: + try: + self.permission = Permission.objects.get( + codename=permission_required, + ) + except ObjectDoesNotExist: + raise AssertionError( + f"!!! Выполнить тесты для урл: {self.path} " + f"невозможно. Проверьте, что в базе данных предусмотрено " + f"разрешение '{permission_required}'.!!!", + ) + + if self.authorized_only: + self.unauthorized_code = unauthorized_code_estimated + else: + self.unauthorized_code = HTTPStatus.OK + self.use_post = use_post + self.request_method = ("GET", "POST")[self.use_post] + self.path_for_message = f"{self.request_method}-запрос по адресу {url}" + self.path = render_url(url, path_render_subs) + + def _get_response(self, client: Client): + if self.use_post: + return client.post(self.path) + return client.get(self.path) + + def _get_auth_response( + self, + client: Client, + user: User, + clear_permissions: bool = True, + clear_admin: bool = True, + ): + if clear_permissions: + user.user_permissions.clear() + if user.is_staff and clear_admin: + user.is_staff = False + user.save() + client.force_login(user) + return self._get_response(client) + + def unauthorized_test(self, client: Client) -> tuple[int, Any, str]: + """ + Возвращает "ответ-ожидание" для неавторизованного пользователя. + + Последним значением возвращает сообщение, которое можно использовать + в тестах. + """ + client.logout() + response = self._get_response(client) + message = ( + f"Для неавторизованного пользователя " + f"{self.path_for_message} должен вернуть ответ со статусом " + f"{self.unauthorized_code}." + ) + return response.status_code, self.unauthorized_code, message + + def user_with_permission_test(self, client: Client, user: User): + """ + Возвращает "ответ-ожидание" для авторизованного пользователя. + + Для авторизованного пользователя, обладающего разрешением с codename, + сохраненным в self.permission. + Последним значением возвращает сообщение, которое можно использовать + в тестах. + """ + if isinstance(self.permission, Permission): + user.user_permissions.add(self.permission) + else: + raise Exception("Ошибка при определении разрешения.") + response = self._get_auth_response(client, user, False) + message = ( + f"Для пользователя, обладающего разрешением " + f"{self.permission.codename}, {self.path_for_message} " + f"должен вернуть ответ со статусом " + f"{self.code_estimated}." + ) + return response.status_code, self.code_estimated, message + + def authorized_test(self, client: Client, user: User): + """ + Возвращает "ответ-ожидание" для авторизованного пользователя. + + Для авторизованного пользователя без каких-либо специфических + разрешений. + """ + response = self._get_auth_response(client, user) + message = ( + f"Для любого авторизованного пользователя, " + f"{self.path_for_message} должен вернуть ответ со " + f"статусом {self.code_estimated}." + ) + return response.status_code, self.code_estimated, message + + def user_without_permission_test(self, client: Client, user: User): + """ + Возвращает "ответ-ожидание" для авторизованного пользователя. + + Для авторизованного пользователя, НЕ обладающего разрешением с + codename, сохраненным в self.permission. + Последним значением возвращает сообщение, которое можно использовать + в тестах. + """ + response = self._get_auth_response( + client, + user, + clear_permissions=True, + ) + if isinstance(self.permission, Permission): + codename = self.permission.codename + else: + codename = "" + message = ( + f"Для пользователя, не обладающего разрешением " + f"{codename}, {self.path_for_message} " + f"должен вернуть ответ со статусом " + f"{HTTPStatus.FORBIDDEN}." + ) + return response.status_code, HTTPStatus.FORBIDDEN, message + + def admin_test(self, client: Client, user: User): + """ + Возвращает "ответ-ожидание" для авторизованного пользователя. + + Для авторизованного пользователя, обладающего полномочиями + администратора (is_staff=true). + Последним значением возвращает сообщение, которое можно использовать + в тестах. + """ + user.is_staff = True + user.save() + response = self._get_auth_response(client, user, clear_admin=False) + message = ( + f"Для пользователя, являющегося администратором (is_staff=True) " + f"{self.path_for_message} должен вернуть ответ со " + f"статусом {self.code_estimated}." + ) + return response.status_code, self.code_estimated, message + + def non_admin_test(self, client: Client, user: User): + """ + Возвращает "ответ-ожидание" для авторизованного пользователя. + + Для авторизованного пользователя, НЕ обладающего полномочиями + администратора (is_staff=False). + Последним значением возвращает сообщение, которое можно использовать + в тестах. + """ + response = self._get_auth_response(client, user) + message = ( + f"Для пользователя, НЕ являющегося администратором (" + f"is_staff=False) {self.path_for_message} должен вернуть " + f"ответ с одним из статусов " + f"{HTTPStatus.FOUND, HTTPStatus.MOVED_PERMANENTLY}." + ) + return ( + response.status_code, + (HTTPStatus.FOUND, HTTPStatus.MOVED_PERMANENTLY), + message, + ) + + def execute_tests(self, client: Client, user: User): + """ + Основной метод класса. + + Возвращает список кортежей (ответ, ожидаемый ответ, сообщение) + для всех вариантов GET-запросов к конкретному url. + """ + res = [self.unauthorized_test(client)] + if self.permission and isinstance(self.permission, Permission): + res.append(self.user_with_permission_test(client, user)) + res.append(self.user_without_permission_test(client, user)) + elif not self.admin_only: + res.append(self.authorized_test(client, user)) + + if self.admin_only: + res.append(self.admin_test(client, user)) + res.append(self.non_admin_test(client, user)) + + return res + + +def render_url(url: str, subs: tuple | str | list): + pattern = re.compile(r"<[^/]+>") + if not re.search(pattern, url): + return url + if isinstance(subs, str): + subs = (subs,) + for sub in subs: + url = re.sub(pattern, sub, url, count=1) + if re.search(pattern, url): + url = re.sub(pattern, subs[-1], url) + return url diff --git a/adaptive_hockey_federation/unloads/__init__.py b/adaptive_hockey_federation/unloads/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/unloads/admin.py b/adaptive_hockey_federation/unloads/admin.py new file mode 100644 index 00000000..8a95e00b --- /dev/null +++ b/adaptive_hockey_federation/unloads/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from unloads.models import Unload + + +class UnloadAdmin(admin.ModelAdmin): + """Админка для модели Выгрузки.""" + + list_display = ("unload_name", "date", "user", "unload_file_slug") + search_fields = ("name",) + ordering = ["date"] + + +admin.site.register(Unload, UnloadAdmin) diff --git a/adaptive_hockey_federation/unloads/apps.py b/adaptive_hockey_federation/unloads/apps.py new file mode 100644 index 00000000..d71f3f31 --- /dev/null +++ b/adaptive_hockey_federation/unloads/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class UnloadsConfig(AppConfig): + """Класс-конфигуратор для приложения unloads.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "unloads" + verbose_name = "Выгрузки" diff --git a/adaptive_hockey_federation/unloads/factories.py b/adaptive_hockey_federation/unloads/factories.py new file mode 100644 index 00000000..7df004d2 --- /dev/null +++ b/adaptive_hockey_federation/unloads/factories.py @@ -0,0 +1,50 @@ +import os +import random +from typing import Tuple + +import factory +from competitions.models import Competition +from core.utils import export_excel +from django.db.models import QuerySet +from main.models import Player, Team +from unloads.models import Unload +from users.factories import UserFactory + + +class UnloadFactory(factory.django.DjangoModelFactory): + """Фабрика для создания тестовых Выгрузок.""" + + class Meta: + model = Unload + + unload_name = factory.Sequence(lambda n: f"Выгрузка{n}") + date = factory.Faker("date_object") + user = factory.SubFactory(UserFactory) + unload_file_slug = factory.LazyAttribute(lambda o: "") + + @factory.post_generation + def create_excel_file(self, create, extracted, **kwargs): + """Метод для создания Excel-файла.""" + if not create: + return + + queryset_with_titles = get_queryset_for_unload(self) + + if queryset_with_titles: + title = queryset_with_titles[1] + queryset = queryset_with_titles[0] + filename = f"{title}_{self.unload_name}.xlsx" + filename = export_excel(queryset, filename, title) + file_path = os.path.join("unloads_data", filename) + self.unload_file_slug = file_path + self.save() + + +def get_queryset_for_unload(unload_instance) -> Tuple[QuerySet, str]: + choices = [ + (Competition.objects.all(), "Выгрузка соревнований"), + (Player.objects.all(), "Выгрузка игроков"), + (Team.objects.all(), "Выгрузка команд"), + ] + selected_choice = random.choice(choices) + return selected_choice diff --git a/adaptive_hockey_federation/unloads/mapping.py b/adaptive_hockey_federation/unloads/mapping.py new file mode 100644 index 00000000..16d741f2 --- /dev/null +++ b/adaptive_hockey_federation/unloads/mapping.py @@ -0,0 +1,16 @@ +model_mapping = { + "players": ("main", "Player", "Данные игроков"), + "teams": ("main", "Team", "Данные команд"), + "competitions": ( + "competitions", + "Competition", + "Данные соревнований", + ), + "analytics": ("main", "Player", "Данные аналитики"), + "unloads": ("unloads", "Unload", "Журнал Выгрузок"), + "users": ( + "users", + "User", + "Данные пользователей", + ), +} diff --git a/adaptive_hockey_federation/unloads/migrations/0001_initial.py b/adaptive_hockey_federation/unloads/migrations/0001_initial.py new file mode 100644 index 00000000..b537ab80 --- /dev/null +++ b/adaptive_hockey_federation/unloads/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.13 on 2024-06-03 19:22 + +import core.constants +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Unload', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('unload_name', models.CharField(default='Выгрузка', max_length=core.constants.MainConstantsInt['CHAR_FIELD_LENGTH'], verbose_name='Имя выгрузки')), + ('date', models.DateField(auto_now_add=True, verbose_name='Дата выгрузки')), + ('unload_file_slug', models.FileField(upload_to='unloads_data/', verbose_name='Ссылка на файл')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')), + ], + options={ + 'verbose_name': 'Выгрузку', + 'verbose_name_plural': 'Выгрузки', + 'ordering': ('date',), + 'permissions': [('list_view_unload', 'Can view list of Выгрузку')], + }, + ), + ] diff --git a/adaptive_hockey_federation/unloads/migrations/__init__.py b/adaptive_hockey_federation/unloads/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/unloads/models.py b/adaptive_hockey_federation/unloads/models.py new file mode 100644 index 00000000..7bb49646 --- /dev/null +++ b/adaptive_hockey_federation/unloads/models.py @@ -0,0 +1,47 @@ +from core.constants import Directory, MainConstantsInt +from django.db import models +from django.db.models.signals import post_delete +from django.dispatch.dispatcher import receiver +from django.utils.translation import gettext_lazy as _ +from users.models import User + + +class Unload(models.Model): + """Выгрузка.""" + + unload_name = models.CharField( + max_length=MainConstantsInt.CHAR_FIELD_LENGTH, + verbose_name=_("Имя выгрузки"), + default="Выгрузка", + ) + date = models.DateField( + auto_now_add=True, + verbose_name="Дата выгрузки", + ) + user = models.ForeignKey( + User, + verbose_name="Пользователь", + on_delete=models.CASCADE, + ) + unload_file_slug = models.FileField( + verbose_name=_("Ссылка на файл"), + upload_to=Directory.UNLOAD_DIR + "/", + ) + + class Meta: + verbose_name = "Выгрузку" + verbose_name_plural = "Выгрузки" + ordering = ("date",) + permissions = [ + ("list_view_unload", "Can view list of Выгрузку"), + ] + + def __str__(self) -> str: + """Метод, использующий unload_name для строкового представления.""" + return f"{self.unload_name}" + + +@receiver(post_delete, sender=Unload) +def document_file_delete(sender, instance, **kwargs): + if instance.unload_file_slug: + instance.unload_file_slug.delete(False) diff --git a/adaptive_hockey_federation/unloads/urls.py b/adaptive_hockey_federation/unloads/urls.py new file mode 100644 index 00000000..d4133463 --- /dev/null +++ b/adaptive_hockey_federation/unloads/urls.py @@ -0,0 +1,22 @@ +from django.urls import include, path +from unloads import views + +app_name = "unloads" + +unloads_urlpattern = [ + path("", views.UnloadListView.as_view(), name="unloads"), + path( + "/", + views.DataExportView.as_view(), + name="data_unloads", + ), + path( + "/delete", + views.DeleteUnloadView.as_view(), + name="delete_unload", + ), +] + +urlpatterns = [ + path("unloads/", include(unloads_urlpattern)), +] diff --git a/adaptive_hockey_federation/unloads/utils.py b/adaptive_hockey_federation/unloads/utils.py new file mode 100644 index 00000000..6ea05feb --- /dev/null +++ b/adaptive_hockey_federation/unloads/utils.py @@ -0,0 +1,135 @@ +from analytics.schema import ANALYTICS_SEARCH_FIELDS +from django.db.models import Q +from main.schemas.player_schema import SEARCH_FIELDS + + +def checking_value(input_value: str) -> str: + """Функция проверки значения. Если цифровое - уберем нули слева.""" + if input_value.isdigit(): + return str(int(input_value)) + return input_value + + +def create_lookup_all(dict_param) -> Q: + """Функция создания запроса по всем полям.""" + or_lookup_all: Q = Q() + search = dict_param["search"][0] + for key, value in SEARCH_FIELDS.items(): + if key == "birthday": + for choice in value: + or_lookup_all |= Q((f"birthday__{choice}__icontains", search)) + else: + or_lookup_all |= Q((f"{value}__icontains", search)) + + return or_lookup_all + + +def players_get_queryset(model, dict_param, queryset): + """Функция создания запроса для игроков.""" + or_lookup: Q = Q() + search_column_name: str = dict_param["search_column"][0] + if search_column_name.lower() in ["все", "all"]: + or_lookup |= create_lookup_all(dict_param) + elif search_column_name == "birthday": + for choice in SEARCH_FIELDS["birthday"]: + if choice in dict_param: + or_lookup &= Q( + (f"birthday__{choice}__exact", int(dict_param[choice][0])), + ) + elif search_column_name == "gender": + if "gender" in dict_param: + or_lookup |= Q(("gender__icontains", dict_param["gender"][0])) + elif search_column_name == "discipline_name": + if "discipline_name" in dict_param: + or_lookup |= Q( + ("discipline_name__name", dict_param["discipline_name"][0]), + ) + else: + search = dict_param["search"][0] + or_lookup |= Q( + (f"{SEARCH_FIELDS[search_column_name]}__icontains", search), + ) + + if queryset: + return queryset.filter(or_lookup) + else: + return model.objects.filter(or_lookup) + + +def analytics_get_queryset(model, dict_param, queryset): + """Функция создания запроса для аналитики.""" + or_lookup: Q = Q() + for key, value in ANALYTICS_SEARCH_FIELDS.items(): + if key in dict_param: + or_lookup &= Q((value, checking_value(dict_param[key][0]))) + + if queryset: + queryset = queryset.filter(or_lookup) + else: + queryset = model.objects.filter(or_lookup) + + return queryset + + +def users_get_queryset(model, dict_param, queryset): + if "search" in dict_param: + search = dict_param["search"][0] + search_column = dict_param["search_column"][0] + if not search_column or search_column.lower() in ["все", "all"]: + or_lookup = ( + Q(first_name__icontains=search) + | Q(last_name__icontains=search) + | Q(patronymic__icontains=search) + | Q(date_joined__icontains=search) + | Q(role__icontains=search) + | Q(email__icontains=search) + | Q(phone__icontains=search) + ) + if queryset: + queryset = queryset.filter(or_lookup) + else: + model.objects.filter(or_lookup) + else: + search_fields = { + "date": "date_joined", + "role": "role", + "email": "email", + "phone": "phone", + } + lookup = {f"{search_fields[search_column]}__icontains": search} + if queryset: + queryset = queryset.filter(**lookup) + else: + queryset = model.objects.filter(**lookup) + return queryset + + +def teams_get_queryset(model, dict_param, queryset): + filter = { + "city": "city__id", + "discipline": "discipline_name__in", + "name": "name__icontains", + } + lookup = {} + for param_key, param_value in dict_param.items(): + if any(len(value) > 0 for value in param_value): + param = filter.get(param_key) + if param is not None: + lookup[param] = param_value[0] + if queryset: + queryset = queryset.filter(**lookup) + else: + queryset = model.objects.filter(**lookup) + + return queryset + + +def model_get_queryset(page_name, model, dict_param, queryset): + if page_name == "users": + return users_get_queryset(model, dict_param, queryset) + elif page_name == "players": + return players_get_queryset(model, dict_param, queryset) + elif page_name == "analytics": + return analytics_get_queryset(model, dict_param, queryset) + elif page_name == "teams": + return teams_get_queryset(model, dict_param, queryset) diff --git a/adaptive_hockey_federation/unloads/views.py b/adaptive_hockey_federation/unloads/views.py new file mode 100644 index 00000000..2db1ccd3 --- /dev/null +++ b/adaptive_hockey_federation/unloads/views.py @@ -0,0 +1,172 @@ +import os +from typing import Any +from urllib.parse import parse_qs, urlparse + +from core.utils import export_excel +from django.apps import apps +from django.conf import settings +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.http import FileResponse +from django.shortcuts import get_object_or_404 +from django.urls import reverse_lazy +from django.views import View +from django.views.generic.edit import DeleteView +from django.views.generic.list import ListView +from unloads.mapping import model_mapping +from unloads.models import Unload +from unloads.utils import model_get_queryset + + +class UnloadListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Список выгрузок.""" + + model = Unload + template_name = "main/unloads/unloads.html" + permission_required = "unloads.list_view_unload" + permission_denied_message = ( + "Отсутствует разрешение на просмотр списка выгрузок." + ) + context_object_name = "unloads" + paginate_by = 10 + ordering = ["date"] + + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + unloads = context["unloads"] + table_data = [] + for unload in unloads: + table_data.append( + { + "pk": unload.pk, + "date": unload.date, + "unload_name": unload.unload_name, + "user": unload.user, + "_ref_": { + "name": "Посмотреть/Excel", + "type": "button", + "url": ( + f"{settings.MEDIA_URL}/{unload.unload_file_slug}" + ), + }, + }, + ) + context["table_head"] = { + "pk": "Nr.", + "date": "Дата", + "unload_name": "Наименование", + "user": "Автор", + "unload_file_slug": "Просмотр/Excel", + } + context["table_data"] = table_data + return context + + +class DeleteUnloadView( + LoginRequiredMixin, + DeleteView, +): + """Удаление выгрузок.""" + + object = Unload + model = Unload + success_url = reverse_lazy("unloads:unloads") + permission_required = "unloads.delete_unload" + permission_denied_message = "Отсутствует разрешение на удаление выгрузки." + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(Unload, id=self.kwargs["pk"]) + + +class DataExportView(LoginRequiredMixin, View): + """Выгрузка данных в Excel.""" + + def get(self, request, *args, **kwargs): + """Обработчиков GET-запросов.""" + page_name = kwargs.get("page_name") + if page_name in model_mapping: + app_label, model_name, title = model_mapping[page_name] + model = apps.get_model(app_label, model_name) + last_url = request.META.get("HTTP_REFERER") + parsed = urlparse(last_url) + params = parse_qs(parsed.query) + if len(params) >= 1: + queryset = model_get_queryset(page_name, model, params, None) + filename = page_name + "_search.xlsx" + else: + queryset = model.objects.all() + filename = page_name + "_all.xlsx" + if model_name == "User": + excluded_fields = ["id", "password", "is_active", "is_staff"] + fields_order = [ + "last_name", + "first_name", + "patronymic", + "email", + "phone", + "role", + "date_joined", + ] + elif model_name == "Player": + excluded_fields = ["id"] + fields_order = [ + "surname", + "name", + "patronymic", + "diagnosis", + "discipline_name", + "discipline_level", + "birthday", + "addition_date", + "gender", + "level_revision", + "position", + "number", + "is_captain", + "is_assistent", + "identity_document", + ] + else: + excluded_fields = [] + fields_order = [] + filename = export_excel( + queryset, + filename, + title, + excluded_fields, + fields_order, + ) + + file_slug = f"unloads_data/{filename}" + + unload_record = Unload( + unload_name=filename, + user=request.user, + unload_file_slug=file_slug, + ) + unload_record.save() + + file_path = os.path.join( + settings.MEDIA_ROOT, + "unloads_data", + filename, + ) + if os.path.exists(file_path): + file_unload = open(file_path, "rb") + response = FileResponse(file_unload) + response["Content-Disposition"] = ( + f'attachment; filename="{filename}"' + ) + return response + else: + raise FileNotFoundError("Файл для выгрузки не найден.") + else: + raise Exception("Для данной страницы выгрузка не предусмотрена.") diff --git a/adaptive_hockey_federation/users/__init__.py b/adaptive_hockey_federation/users/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/users/admin.py b/adaptive_hockey_federation/users/admin.py new file mode 100644 index 00000000..b1b5c491 --- /dev/null +++ b/adaptive_hockey_federation/users/admin.py @@ -0,0 +1,98 @@ +from django.contrib import admin +from django.contrib.auth.admin import GroupAdmin as DjangoGroupAdmin +from django.contrib.auth.models import Group, Permission +from users.forms import GroupAdminForm +from users.models import ProxyGroup, User + + +@admin.register(User) +class UserAdmin(admin.ModelAdmin): + """Модель пользователя для административной панели Django.""" + + list_display = ( + "id", + "last_name", + "first_name", + "email", + "phone", + "is_staff", + "is_superuser", + ) + list_filter = ( + "groups", + "is_active", + ) + fields = [ + "last_name", + ("first_name", "patronymic"), + "password", + "email", + "phone", + "is_staff", + "is_superuser", + "role", + ] + empty_value_display = "-пусто-" + + @admin.display(description="Роль") + def role(self, obj): + """Добавить поле role в отображение объекта в админке.""" + return obj.groups.all()[:1] + + def get_queryset(self, request): + """Получить набор QuerySet.""" + return super().get_queryset(request).prefetch_related("groups") + + def save_model(self, request, obj, form, change): + """Сохранить данные модели.""" + if form.cleaned_data.get("password"): + obj.set_password(form.cleaned_data["password"]) + super().save_model(request, obj, form, change) + + +class HiddenAdmin(DjangoGroupAdmin): + """ + Удаление модели из панели администратора. + + Необходимость регистрации модели в панели администратора + вызвана применением пакета django-filer + """ + + def has_module_permission(self, request): + """Определить имеет ли модуль разрешение.""" + return False + + +admin.site.unregister(Group) +admin.site.register(Group, HiddenAdmin) + + +class GroupAdmin(admin.ModelAdmin): + """Модель групп для административной панели Django.""" + + form = GroupAdminForm + list_display = ["name"] + filter_horizontal = ("permissions",) + + +admin.site.register(ProxyGroup, GroupAdmin) + + +def permissions_new_unicode(self): + # Перевод настроек доступа + class_name = str(self.content_type) + permissions_name = str(self.name) + + if "Can delete" in permissions_name: + permissions_name = "разрешено удалять" + elif "Can add" in permissions_name: + permissions_name = "разрешено добавлять" + elif "Can change" in permissions_name: + permissions_name = "разрешено изменять" + elif "Can view" in permissions_name: + permissions_name = "разрешено просматривать" + + return "%s - %s" % (class_name.title(), permissions_name) + + +Permission.__str__ = permissions_new_unicode # type: ignore diff --git a/adaptive_hockey_federation/users/apps.py b/adaptive_hockey_federation/users/apps.py new file mode 100644 index 00000000..3b4b8368 --- /dev/null +++ b/adaptive_hockey_federation/users/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + """Класс-конфигуратор для приложения users.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "users" + verbose_name = "Пользователи" diff --git a/adaptive_hockey_federation/users/constants.py b/adaptive_hockey_federation/users/constants.py new file mode 100644 index 00000000..cb179f46 --- /dev/null +++ b/adaptive_hockey_federation/users/constants.py @@ -0,0 +1,55 @@ +from core.constants import Group + + +class GroupPermission: + """Разрешения для групп пользователей.""" + + GROUP_PERMISSION = { + Group.ADMINS: ( + "exclude", + ( + "add_logentry", + "change_logentry", + "delete_logentry", + "view_logentry", + "add_contenttype", + "change_contenttype", + "delete_contenttype", + "view_contenttype", + "add_session", + "change_session", + "delete_session", + "view_session", + ), + ), + Group.MODERATORS: ( + "include", + ( + "change_player", + "view_player", + "add_document", + "change_document", + "delete_document", + "view_document", + ), + ), + Group.AGENTS: ( + "include", + ( + "change_player", + "view_player", + "add_player", + "list_view_player", + "change_team", + "view_team", + "list_view_team", + "add_document", + "change_document", + "delete_document", + "view_document", + ), + ), + } + + +REGEX_AREA_CODE_IS_SEVEN_HUNDRED = "^7[0-9]{2,10}$" diff --git a/adaptive_hockey_federation/users/factories.py b/adaptive_hockey_federation/users/factories.py new file mode 100644 index 00000000..6041c50e --- /dev/null +++ b/adaptive_hockey_federation/users/factories.py @@ -0,0 +1,37 @@ +import factory # type: ignore +from django.contrib.auth import get_user_model +from django.contrib.auth.hashers import make_password +from faker import Faker +from users.provaders import CustomPhoneProvider + +fake = Faker(locale="ru_RU") +fake.add_provider(CustomPhoneProvider) +User = get_user_model() + + +class UserFactory(factory.django.DjangoModelFactory): + """Фабрика создания тестовых юзеров.""" + + class Meta: + model = User + skip_postgeneration_save = True + + first_name = factory.Faker("first_name", locale="ru_RU") + last_name = factory.Faker("last_name", locale="ru_RU") + patronymic = factory.Faker("first_name", locale="ru_RU") + email = factory.Faker("email", locale="ru_RU") + phone = factory.LazyAttribute(lambda _: fake.phone_number()) + + @factory.lazy_attribute + def password(self): + """Метод для создания пароля.""" + return make_password("pass1234") + + @factory.post_generation + def admin_create(self, create, extracted, **kwargs): + """Метод для присваивания роли админа или модератора.""" + if create: + if self.role in ["admin", "moderator"]: + self.is_staff = True + if self.role == "admin": + self.is_superuser = True diff --git a/adaptive_hockey_federation/users/forms.py b/adaptive_hockey_federation/users/forms.py new file mode 100644 index 00000000..6fe79391 --- /dev/null +++ b/adaptive_hockey_federation/users/forms.py @@ -0,0 +1,208 @@ +import unicodedata + +from core.constants import FORM_HELP_TEXTS +from django import forms +from django.contrib.admin.widgets import FilteredSelectMultiple +from django.contrib.auth import get_user_model +from django.contrib.auth.forms import UserChangeForm, UserCreationForm +from django.contrib.auth.models import Group +from django.core.exceptions import ValidationError +from django.utils.crypto import get_random_string +from main.models import Team + +User = get_user_model() + + +class EmailField(forms.EmailField): + """Класс для расширения поля email.""" + + def to_python(self, value): + """Преобразовать значение в знакомый для Python объект.""" + value = super().to_python(value) + return None if value is None else unicodedata.normalize("NFKC", value) + + +class GroupAdminForm(forms.ModelForm): + """Дополнительное поле "Пользователи" для групп.""" + + class Meta: + model = Group + fields = ["name", "users", "permissions"] + + users = forms.ModelMultipleChoiceField( + queryset=User.objects.all(), + required=False, + widget=FilteredSelectMultiple("пользователи", False), + label="Пользователи", + ) + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + super().__init__(*args, **kwargs) + if self.instance.pk: + self.fields["users"].initial = self.instance.user_set.all() + + def save_m2m(self): + """Метод устанавливает M2M связи между группой и пользователями.""" + self.instance.user_set.through.objects.filter( + user__in=self.cleaned_data["users"], + ).delete() + + self.instance.user_set.set(self.cleaned_data["users"]) + + def save(self, *args, **kwargs): + """Метод создает и сохраняет объект в базе данных.""" + instance = super().save() + self.save_m2m() + return instance + + +class UserAdminForm(UserChangeForm): + """Форма для обновления пользователя через админку.""" + + email = EmailField(label="Электронная почта", required=True) + is_staff = forms.CharField(widget=forms.HiddenInput()) + is_superuser = forms.CharField(widget=forms.HiddenInput()) + + def save(self, commit=True): + """Метод создает и сохраняет объект в базе данных.""" + user = super().save(commit=False) + user.email = self.cleaned_data["email"] + return user + + def clean(self): + """Метод для валидации данных полей формы.""" + groups = self.cleaned_data["groups"] + if groups.count() > 1: + raise ValidationError("Выбрать можно только одну группу.") + return super().clean() + + +class UserAdminCreationForm(UserCreationForm): + """Форма для создания пользователя через админку.""" + + email = forms.EmailField( + label="Email", + help_text=( + "Обязательное поле. На данную почту пользователю " + "будет выслана ссылка для смены и восстановления пароля." + ), + required=True, + ) + first_name = forms.CharField(label="Имя", help_text="Обязательное поле") + last_name = forms.CharField(label="Фамилия", help_text="Обязательное поле") + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + super().__init__(*args, **kwargs) + self.fields["password1"].required = False + self.fields["password2"].required = False + + def save(self, commit=True): + """Метод создает и сохраняет объект в базе данных.""" + user = super().save(commit=False) + user.email = self.cleaned_data["email"] + user.set_password(get_random_string(length=8)) + user.save() + return user + + class Meta: + model = User + fields = ( + "first_name", + "last_name", + "email", + ) + error_messages = { + "email": { + "unique": "Электронная почта должна быть уникальной!", + }, + } + + +class CustomUserCreateForm(forms.ModelForm): + """Форма для создания пользователя.""" + + team = forms.ModelMultipleChoiceField( + queryset=Team.objects.all(), + required=False, + label="Команды", + ) + + class Meta: + model = User + fields = ( + "first_name", + "last_name", + "patronymic", + "email", + "phone", + "role", + "team", + ) + widgets = { + "first_name": forms.TextInput( + attrs={"placeholder": "Введите фамилию (обязательно)"}, + ), + "last_name": forms.TextInput( + attrs={"placeholder": "Введите Имя (обязательно)"}, + ), + "patronymic": forms.TextInput( + attrs={"placeholder": "Введите отчество"}, + ), + "email": forms.EmailInput( + attrs={"placeholder": "Введите email (обязательно)"}, + ), + "phone": forms.TextInput( + attrs={"placeholder": "Введите номер игрока"}, + ), + } + help_texts = { + "email": FORM_HELP_TEXTS["email"], + "role": FORM_HELP_TEXTS["role"], + } + + def clean_team(self): + """Проверка команды при создании пользователя.""" + busy_teams = None + choice_team = None + if choice_team := self.cleaned_data["team"]: + busy_teams = [ + team for team in choice_team if team.curator is not None + ] + if len(busy_teams) > 0: + message = [ + f"У команды {team.name} уже есть куратор {team.curator}" + for team in busy_teams + ] + raise ValidationError(message) + return choice_team + + +class CustomUserUpdateForm(CustomUserCreateForm): + """Форма для обновления пользователя.""" + + def __init__(self, *args, **kwargs): + """Метод инициализации экземпляра класса.""" + super(CustomUserUpdateForm, self).__init__(*args, **kwargs) + if self.instance.team.all(): + self.fields["team"].initial = self.instance.team.all() + + def clean_team(self): + """Проверка команды при создании пользователя.""" + busy_teams = None + choice_teams = None + current_teams = list(self.instance.team.all()) + if choice_teams := self.cleaned_data["team"]: + busy_teams = [ + team for team in choice_teams if team.curator is not None + ] + if len(current_teams) > 0: + busy_teams = list(set(busy_teams) - set(current_teams)) + if len(busy_teams) > 0: + message = [ + f"У команды {team.name} уже есть куратор {team.curator}" + for team in busy_teams + ] + raise ValidationError(message) + return choice_teams diff --git a/adaptive_hockey_federation/users/managers.py b/adaptive_hockey_federation/users/managers.py new file mode 100644 index 00000000..fa448a8a --- /dev/null +++ b/adaptive_hockey_federation/users/managers.py @@ -0,0 +1,40 @@ +from core.constants import Role +from django.contrib.auth.base_user import BaseUserManager +from django.utils.translation import gettext_lazy as _ + + +class CustomUserManager(BaseUserManager): + """ + Кастомный менеджер модели пользователя. + + Идентификатором является поле с адресом электронной почты. + """ + + use_in_migrations = True + + def _create_user(self, email, password, **extra_fields): + if not email: + raise ValueError(_("Предоставить адрес электронной почты.")) + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save() + return user + + def create_user(self, email, password=None, **extra_fields): + """Создать пользователя.""" + extra_fields.setdefault("is_staff", False) + extra_fields.setdefault("is_superuser", False) + return self._create_user(email, password, **extra_fields) + + def create_superuser(self, email, password, **extra_fields): + """Создать суперпользователя.""" + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + extra_fields["role"] = Role.SUPERUSER + if extra_fields.get("is_staff") is not True: + raise ValueError(_("Суперюзер должен иметь is_staff=True.")) + if extra_fields.get("is_superuser") is not True: + raise ValueError(_("Суперюзер должен иметь is_superuser=True.")) + + return self._create_user(email, password, **extra_fields) diff --git a/adaptive_hockey_federation/users/migrations/0001_initial.py b/adaptive_hockey_federation/users/migrations/0001_initial.py new file mode 100644 index 00000000..1367161d --- /dev/null +++ b/adaptive_hockey_federation/users/migrations/0001_initial.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.13 on 2024-06-03 19:22 + +import core.constants +import django.contrib.auth.models +import django.core.validators +import django.utils.timezone +import phonenumber_field.modelfields +import phonenumber_field.validators +import users.managers +import users.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='ProxyGroup', + fields=[ + ], + options={ + 'verbose_name': 'Группа', + 'verbose_name_plural': 'Группы', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('auth.group',), + managers=[ + ('objects', django.contrib.auth.models.GroupManager()), + ], + ), + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('first_name', models.CharField(help_text='Имя', max_length=core.constants.UserConstans['NAME_MAX_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Имя')), + ('last_name', models.CharField(help_text='Фамилия', max_length=core.constants.UserConstans['NAME_MAX_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Фамилия')), + ('patronymic', models.CharField(blank=True, help_text='Отчество', max_length=core.constants.UserConstans['NAME_MAX_LENGTH'], validators=[django.core.validators.RegexValidator('^[А-Яа-яё -]+$', 'Строка должны состоять из кирилических символов. Возможно использование дефиса.')], verbose_name='Отчество')), + ('role', models.CharField(choices=[(core.constants.Role['AGENT'], 'Представитель команды'), (core.constants.Role['MODERATOR'], 'Модератор'), (core.constants.Role['ADMIN'], 'Администратор')], default=core.constants.Role['AGENT'], help_text='Уровень прав доступа', max_length=21, verbose_name='Роль')), + ('email', models.EmailField(help_text='Электронная почта', max_length=core.constants.UserConstans['NAME_MAX_LENGTH'], unique=True, validators=[django.core.validators.EmailValidator(message='Используйте корректный адрес электронной почты. Адрес должен быть не длиннее 150 символов. Допускается использование латинских букв, цифр и символов @/./+/-/_')], verbose_name='Электронная почта')), + ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ', max_length=128, region=None, validators=[phonenumber_field.validators.validate_international_phonenumber, users.validators.zone_code_without_seven_hundred], verbose_name='Актуальный номер телефона')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Дата регистрации')), + ('is_staff', models.BooleanField(default=False, verbose_name='Статус администратора.')), + ('is_active', models.BooleanField(default=True, verbose_name='Показывает статус он-лайн.')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'Пользователь', + 'verbose_name_plural': 'Пользователи', + 'ordering': ('last_name',), + 'permissions': [('list_view_user', 'Can view list of Пользователь')], + }, + managers=[ + ('objects', users.managers.CustomUserManager()), + ], + ), + ] diff --git a/adaptive_hockey_federation/users/migrations/0002__setting_group.py b/adaptive_hockey_federation/users/migrations/0002__setting_group.py new file mode 100644 index 00000000..79bace58 --- /dev/null +++ b/adaptive_hockey_federation/users/migrations/0002__setting_group.py @@ -0,0 +1,46 @@ +from core.constants import GROUPS_BY_ROLE +from django.contrib.auth.management import create_permissions +from django.db import migrations +from users.constants import GroupPermission + + +def set_default_groups(apps, schema_editor): + Group = apps.get_model("users", "ProxyGroup") + Group.objects.bulk_create( + [Group(name=name) for name in set(GROUPS_BY_ROLE.values())] + ) + + +def set_permissions_to_groups(apps, schema_editor): + for app_config in apps.get_app_configs(): + app_config.models_module = True + create_permissions(app_config, verbosity=0) + del app_config.models_module + + Group = apps.get_model("users", "ProxyGroup") + Permission = apps.get_model("auth", "Permission") + + for group in Group.objects.all(): + if GroupPermission.GROUP_PERMISSION[group.name][0] == "include": + for codename in GroupPermission.GROUP_PERMISSION[group.name][1]: + group.permissions.add(Permission.objects.get(codename=codename)) + continue + for perm in Permission.objects.all(): + if perm.codename not in GroupPermission.GROUP_PERMISSION[group.name][1]: + group.permissions.add(perm) + group.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0001_initial"), + ] + + operations = [ + migrations.RunPython( + set_default_groups, + ), + migrations.RunPython( + set_permissions_to_groups, + ), + ] diff --git a/adaptive_hockey_federation/users/migrations/__init__.py b/adaptive_hockey_federation/users/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/users/models.py b/adaptive_hockey_federation/users/models.py new file mode 100644 index 00000000..c20d1251 --- /dev/null +++ b/adaptive_hockey_federation/users/models.py @@ -0,0 +1,227 @@ +from typing import Iterable + +from core.constants import GROUPS_BY_ROLE, ROLES_CHOICES, Role, UserConstans +from core.validators import fio_validator +from django.contrib.auth.models import ( + AbstractBaseUser, + Group, + PermissionsMixin, +) +from django.core.exceptions import ValidationError +from django.core.mail import send_mail +from django.core.validators import EmailValidator +from django.db import models +from django.http import Http404 +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from phonenumber_field.modelfields import PhoneNumberField +from phonenumber_field.validators import validate_international_phonenumber +from users.managers import CustomUserManager +from users.validators import zone_code_without_seven_hundred + + +class User(AbstractBaseUser, PermissionsMixin): + """ + Кастомная модель пользователя. + + 1. Поле 'username' исключено + 2. Идентификатором является поле с адресом электронной почты. + """ + + first_name = models.CharField( + max_length=UserConstans.NAME_MAX_LENGTH, + verbose_name=_("Имя"), + help_text=_("Имя"), + validators=[fio_validator()], + ) + last_name = models.CharField( + max_length=UserConstans.NAME_MAX_LENGTH, + verbose_name=_("Фамилия"), + help_text=_("Фамилия"), + validators=[fio_validator()], + ) + patronymic = models.CharField( + blank=True, + max_length=UserConstans.NAME_MAX_LENGTH, + verbose_name=_("Отчество"), + help_text=_("Отчество"), + validators=[fio_validator()], + ) + role = models.CharField( + choices=ROLES_CHOICES, + default=Role.AGENT, + max_length=max(len(role) for role, _ in ROLES_CHOICES), + verbose_name=_("Роль"), + help_text=_("Уровень прав доступа"), + ) + email = models.EmailField( + max_length=UserConstans.EMAIL_MAX_LENGTH, + unique=True, + validators=( + EmailValidator( + message="Используйте корректный адрес электронной почты. " + "Адрес должен быть не длиннее 150 символов. " + "Допускается использование латинских букв, " + "цифр и символов @/./+/-/_", + ), + ), + verbose_name=_("Электронная почта"), + help_text=_("Электронная почта"), + ) + phone = PhoneNumberField( + blank=True, + validators=[ + validate_international_phonenumber, + zone_code_without_seven_hundred, + ], + verbose_name=_("Актуальный номер телефона"), + help_text=_("Номер телефона, допустимый формат - +7 ХХХ ХХХ ХХ ХХ"), + ) + date_joined = models.DateTimeField( + default=timezone.now, + verbose_name=_("Дата регистрации"), + ) + is_staff = models.BooleanField( + default=False, + verbose_name=_("Статус администратора."), + ) + is_active = models.BooleanField( + default=True, + verbose_name=_("Показывает статус он-лайн."), + ) + + objects = CustomUserManager() + + EMAIL_FIELD = "email" + USERNAME_FIELD = "email" + REQUIRED_FIELDS = ["first_name", "last_name", "role"] + + class Meta: + verbose_name = _("Пользователь") + verbose_name_plural = _("Пользователи") + ordering = ("last_name",) + permissions = [ + ("list_view_user", "Can view list of Пользователь"), + ] + + def clean(self): + """Метод валидации модели.""" + super().clean() + self.email = self.__class__.objects.normalize_email(self.email) + + def __str__(self): + """Использует метод get_initials() для строкового представления.""" + return self.get_initials() + + def get_initials(self) -> str: + """ + Возвращает фамилию и инициалы пользователя. + + При отсутствии отчества возвращается фамилия и инициал имени. + """ + name_i = self.first_name[:1].upper() + "." + if patronymic_i := self.patronymic[:1]: + patronymic_i = patronymic_i.upper() + "." + return f"{self.last_name} {name_i} {patronymic_i}" + + def get_full_name(self) -> str: + """Получает полную строку с ФИО пользователя.""" + return ( + f"{self.last_name[:UserConstans.QUERY_SET_LENGTH]} " + f"{self.first_name[:UserConstans.QUERY_SET_LENGTH]} " + f"{self.patronymic[:UserConstans.QUERY_SET_LENGTH]}" + ) + + def email_user(self, subject, message, from_email=None, **kwargs): + """Отправляет email пользователю с заданным сообщением.""" + send_mail(subject, message, from_email, [self.email], **kwargs) + + def save( + self, + force_insert: bool = False, # type: ignore[override] + force_update: bool = False, + using: str | None = None, + update_fields: Iterable[str] | None = None, + ) -> None: + """ + Переопределенный метод модели. + + При любом сохранении устанавливает группу пользователя в зависимости + от его роли. + """ + super().save( + force_insert, + force_update, + using, + update_fields, + ) # type: ignore + self.set_group() + + @property + def is_agent(self): + """ + Установка атрибута is_agent. + + Представитель команды - имеет возможность редактировать данные детей в + своей команде. Просматривать некоторые данные по игрокам в других + командах (ФИО, возраст, спортивный класс, тип заболевания). + Выгружать формы по своей команде. Загружать сканы справок. + """ + return self.role == Role.AGENT + + @property + def is_moderator(self): + """ + Установка атрибута is_moderator. + + Модератор - имеет возможности вносить данные по определенному ребенку, + не может добавлять новых пользователей и удалять детей, команды. + """ + return self.role == Role.MODERATOR + + @property + def is_admin(self): + """Администратор - имеет неограниченные права управления на проекте.""" + return self.role == Role.ADMIN or self.is_staff + + def set_group(self): + """Добавляет пользователя в группу в зависимости от его роли.""" + if not (group_name := GROUPS_BY_ROLE.get(self.role, None)): + raise ValidationError( + f"Неизвестная роль пользователя: " f"{self.role}", + ) + if group := ProxyGroup.get_by_name(group_name): + self.groups.clear() + self.groups.add(group) + else: + raise Http404(f"Не найдена группа {group_name}") + + +class ProxyGroup(Group): + """ + Обычная группа django. + + Класс необходим для регистрации модели в приложении "пользователи". + """ + + class Meta: + proxy = True + verbose_name = "Группа" + verbose_name_plural = "Группы" + + def __str__(self): + """Метод, использующий поле name для строкового представления.""" + return self.name + + @classmethod + def get_by_name(cls, name: str) -> Group | None: + """ + Возвращает группу по полю "name". + + Предварительно проверяет наличие таковой группы. + ВНИМАНИЕ!!! Метод не вызывает исключения при отсутствии в БД искомой + группы, а вместо исключения возвращает None. + """ + if (obj := cls.objects.filter(name=name)).exists(): + return obj.first() + return None diff --git a/adaptive_hockey_federation/users/provaders.py b/adaptive_hockey_federation/users/provaders.py new file mode 100644 index 00000000..9d3ada56 --- /dev/null +++ b/adaptive_hockey_federation/users/provaders.py @@ -0,0 +1,28 @@ +import re + +import phonenumbers +from core.config.base_settings import PHONENUMBER_DEFAULT_REGION +from faker.providers.phone_number.ru_RU import Provider +from users.constants import REGEX_AREA_CODE_IS_SEVEN_HUNDRED + + +class CustomPhoneProvider(Provider): + """Класс провайдера для телефона.""" + + def phone_number(self): + """Получить номер телефона.""" + while True: + phone_number = self.numerify(self.random_element(self.formats)) + parsed_number = phonenumbers.parse( + phone_number, + PHONENUMBER_DEFAULT_REGION, + ) + if not re.search( + REGEX_AREA_CODE_IS_SEVEN_HUNDRED, + str(parsed_number.national_number), + ): + if phonenumbers.is_valid_number(parsed_number): + return phonenumbers.format_number( + parsed_number, + phonenumbers.PhoneNumberFormat.INTERNATIONAL, + ) diff --git a/adaptive_hockey_federation/users/urls.py b/adaptive_hockey_federation/users/urls.py new file mode 100644 index 00000000..08693cfe --- /dev/null +++ b/adaptive_hockey_federation/users/urls.py @@ -0,0 +1,42 @@ +from django.urls import include, path +from users.views import ( + CreateUserView, + DeleteUserView, + PasswordSetView, + UpdateUserView, + UsersListView, +) + +app_name = "users" + +users_urlpatterns = [ + path( + "", + UsersListView.as_view(), + name="users", + ), + path( + "create/", + CreateUserView.as_view(), + name="user_create", + ), + path( + "/edit/", + UpdateUserView.as_view(), + name="user_update", + ), + path( + "/delete/", + DeleteUserView.as_view(), + name="user_delete", + ), + path( + "set_password///", + PasswordSetView.as_view(), + name="password_set", + ), +] + +urlpatterns = [ + path("users/", include(users_urlpatterns)), +] diff --git a/adaptive_hockey_federation/users/utilits/__init__.py b/adaptive_hockey_federation/users/utilits/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adaptive_hockey_federation/users/utilits/create_password.py b/adaptive_hockey_federation/users/utilits/create_password.py new file mode 100644 index 00000000..2f0f7e77 --- /dev/null +++ b/adaptive_hockey_federation/users/utilits/create_password.py @@ -0,0 +1,8 @@ +import random +import string + + +def generate_random_password(length: int = 12) -> str: + """Функция генерации случайного пароля пользователя.""" + characters = string.ascii_letters + string.digits + string.punctuation + return "".join(random.choice(characters) for _ in range(length)) diff --git a/adaptive_hockey_federation/users/utilits/render.py b/adaptive_hockey_federation/users/utilits/render.py new file mode 100644 index 00000000..70de3a28 --- /dev/null +++ b/adaptive_hockey_federation/users/utilits/render.py @@ -0,0 +1,22 @@ +from typing import Any, Dict, List + +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string + + +def render_email_message( + subject: str, + context: Dict[str, Any], + from_email: str, + to: List[str], + template: str, +) -> EmailMultiAlternatives: + """Функция визуализации электронного письма из html-шаблона.""" + html_body = render_to_string(template, context) + email = EmailMultiAlternatives( + subject=subject, + from_email=from_email, + to=to, + ) + email.attach_alternative(html_body, "text/html") + return email diff --git a/adaptive_hockey_federation/users/utilits/send_mails.py b/adaptive_hockey_federation/users/utilits/send_mails.py new file mode 100644 index 00000000..56922035 --- /dev/null +++ b/adaptive_hockey_federation/users/utilits/send_mails.py @@ -0,0 +1,126 @@ +import logging +import os +import sys + +from competitions.models import Competition +from core.config import dev_settings +from django.contrib.auth.tokens import default_token_generator +from django.core.mail import send_mail +from django.urls import reverse +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode +from main.models import Team +from users.models import User +from users.utilits.render import render_email_message + + +def send_password_reset_email( + instance: User, + message: str | None = None, + template: str | None = None, +) -> None: + """Отправка письма с ссылкой восстановления пароля.""" + if template is None: + template = "emailing/password_reset_email.html" + reset_link = get_password_reset_link(instance) + email = render_email_message( + subject="Доступ к аккаунту пользователя", + context={ + "password_reset_link": reset_link, + "message": message, + "user": instance, + }, + from_email=dev_settings.EMAIL_HOST_USER, + to=[ + instance.email, + ], + template=template, + ) + email.send(fail_silently=False) + + +def get_password_reset_link(instance: User) -> str: + """Функция генерации ссылки для смены пароля.""" + uid = urlsafe_base64_encode(force_bytes(instance.pk)) + token = default_token_generator.make_token(instance) + reset_url = reverse("users:password_set", args=[uid, token]) + return ( + f"http://{os.environ.get('HOST', '127.0.0.1')}:" + f"{os.environ.get('PORT', '8000')}{reset_url}" + ) + + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setLevel(logging.DEBUG) + +formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +console_handler.setFormatter(formatter) + +logger.addHandler(console_handler) + + +def send_welcome_mail( + team: Team, + competition: Competition, + curator_email: str, +) -> None: + """Отправка пригласительного письма.""" + template = "emailing/welcome_letter.html" + link = reverse( + "competitions:competition_id", + kwargs={"pk": competition.pk}, + ) + try: + email = render_email_message( + subject="Пригласительное письмо", + context={ + "instance": team, + "competition": competition, + "link_to_info": f"http://{os.environ.get('HOST', '127.0.0.1')}:" # noqa + f"{os.environ.get('PORT', '8000')}{link}", + }, + from_email=dev_settings.EMAIL_HOST_USER, + to=[ + curator_email, + ], + template=template, + ) + email.send(fail_silently=False) + logger.info( + f"Электронное письмо успешно отправлено на адрес {curator_email}", + ) + except Exception as e: + logger.error( + "Произошла ошибка при отправке электронного письма" + f" на {curator_email}: {e}", + ) + + +def send_info_mail( + subject: str, + context: str, + user_email: str, +) -> None: + """Отправка информационного письма.""" + try: + send_mail( + subject, + context, + dev_settings.EMAIL_HOST_USER, + [user_email], + fail_silently=False, + ) + except Exception as e: + logger.error( + "Произошла ошибка при отправке электронного письма" + f" на {user_email}: {e}", + ) + else: + logger.info( + f"Электронное письмо успешно отправлено на адрес {user_email}", + ) diff --git a/adaptive_hockey_federation/users/validators.py b/adaptive_hockey_federation/users/validators.py new file mode 100644 index 00000000..eb524c36 --- /dev/null +++ b/adaptive_hockey_federation/users/validators.py @@ -0,0 +1,13 @@ +import re + +from django.core.exceptions import ValidationError + + +def zone_code_without_seven_hundred(phone): + pattern = r"^(8\s?\(?7|\+?7\s?\(?7).*$" + + if re.match(pattern, phone): + raise ValidationError( + "Выберете код в 8 (***) в из следующих вариантов:" + "3**, 4**, 8**, 9**.", + ) diff --git a/adaptive_hockey_federation/users/views.py b/adaptive_hockey_federation/users/views.py new file mode 100644 index 00000000..ba86532a --- /dev/null +++ b/adaptive_hockey_federation/users/views.py @@ -0,0 +1,201 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.mixins import ( + LoginRequiredMixin, + PermissionRequiredMixin, +) +from django.contrib.auth.views import PasswordResetConfirmView +from django.db.models import Q +from django.shortcuts import get_object_or_404 +from django.urls import reverse, reverse_lazy +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.list import ListView +from main.models import Team +from users.forms import CustomUserCreateForm, CustomUserUpdateForm +from users.utilits.send_mails import send_password_reset_email + +User = get_user_model() + + +class UsersListView( + LoginRequiredMixin, + PermissionRequiredMixin, + ListView, +): + """Представление для получения списка пользователей.""" + + model = User + template_name = "main/users/list.html" + permission_required = "users.list_view_user" + permission_denied_message = ( + "Отсутствует разрешение на просмотр списка пользователей." + ) + context_object_name = "users" + paginate_by = 10 + + def get_queryset(self): + """Получить набор QuerySet.""" + queryset = super().get_queryset().order_by("last_name") + search_params = self.request.GET.dict() + search_column = search_params.get("search_column") + search = search_params.get("search") + if search_column: + if search_column and search_column.lower() in ["все", "all"]: + or_lookup = ( + Q(first_name__icontains=search) + | Q(last_name__icontains=search) + | Q(patronymic__icontains=search) + | Q(role__icontains=search) + | Q(email__icontains=search) + | Q(phone__icontains=search) + | Q(date_joined__icontains=search) + ) + queryset = queryset.filter(or_lookup) + elif search_column == "name": + queryset = queryset.filter( + Q(first_name__icontains=search) + | Q(last_name__icontains=search) + | Q(patronymic__icontains=search), + ) + elif search_column == "date": + queryset = queryset.filter( + Q(date_joined__year__icontains=search_params["year"]) + & Q( + date_joined__month__icontains=search_params[ + "month" + ].lstrip("0"), + ) + & Q( + date_joined__day__icontains=search_params[ + "day" + ].lstrip("0"), + ), + ) + else: + search_fields = { + "role": "role", + "email": "email", + "phone": "phone", + } + queryset = queryset.filter( + **{f"{search_fields[search_column]}__icontains": search}, + ) + return queryset.order_by("last_name") + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + users = context["users"] + table_data = [] + for user in users: + table_data.append( + { + "name": user.get_full_name(), + "date": user.date_joined, + "role": user.get_role_display(), + "email": user.email, + "phone": user.phone, + "id": user.pk, + }, + ) + context["table_head"] = { + "name": "Имя", + "date": "Дата регистрации", + "role": "Роль", + "email": "Email", + "phone": "Телефон", + } + context["table_data"] = table_data + return context + + +class UpdateUserView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """Представление для обновления пользователя.""" + + model = User + form_class = CustomUserUpdateForm + template_name = "main/users/user_create_edit.html" + permission_required = "users.change_user" + permission_denied_message = ( + "Отсутствует разрешение на изменение пользователя." + ) + + def get_success_url(self): + """Перенаправить на указанный адрес при успешном обновлении.""" + return reverse("users:users") + + def get_object(self, queryset=None): + """Получить объект по id или выбросить ошибку 404.""" + return get_object_or_404(User, id=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + context["page_title"] = "Редактирование профиля пользователя" + return context + + def form_valid(self, form): + """Запустить валидацию формы.""" + if form.is_valid(): + user = form.save() + Team.objects.filter(curator=user).update(curator=None) + choice_teams = form.cleaned_data["team"] + if choice_teams is not None: + for team in choice_teams: + team.curator = user + team.save() + return super().form_valid(form) + + +class DeleteUserView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): + """Представление для удаления пользователя.""" + + object = User + model = User + success_url = "/users" + permission_required = "users.delete_user" + permission_denied_message = ( + "Отсутствует разрешение на удаление пользователей." + ) + + +class CreateUserView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """Представление для создания пользователя.""" + + model = User + form_class = CustomUserCreateForm + template_name = "main/users/user_create_edit.html" + success_url = "/users" + permission_required = "users.add_user" + permission_denied_message = ( + "Отсутствует разрешение на создание пользователей." + ) + + def get_context_data(self, **kwargs): + """Получить словарь context для шаблона страницы.""" + context = super().get_context_data(**kwargs) + context["page_title"] = "Создание пользователя" + return context + + def form_valid(self, form): + """Запустить валидацию формы.""" + if form.is_valid(): + user = form.save() + choice_teams = form.cleaned_data["team"] + if choice_teams is not None: + for team in choice_teams: + team.curator = user + team.save() + send_password_reset_email(user) + return super().form_valid(form) + + +class PasswordSetView(PasswordResetConfirmView): + """Представление для изменения пароля пользователя.""" + + success_url = reverse_lazy("users:users") + + def form_valid(self, form): + """Запустить валидацию формы.""" + response = super().form_valid(form) + self.user.save() + return response diff --git a/docs/ER_Diagram.drawio.jpg b/docs/ER_Diagram.drawio.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3ffa2d0774545f33f3e5230390882c2d5d3e0dc4 GIT binary patch literal 278946 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

oNAsOWnA2z18(G{%(h7OPw|};0r+;^mkq+$Dar* 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 literal 0 HcmV?d00001 diff --git a/docs/ER_Diagram.drawio.xml b/docs/ER_Diagram.drawio.xml new file mode 100644 index 00000000..9a2e9b6d --- /dev/null +++ b/docs/ER_Diagram.drawio.xml @@ -0,0 +1,2916 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/codestyle.md b/docs/codestyle.md new file mode 100644 index 00000000..864780fa --- /dev/null +++ b/docs/codestyle.md @@ -0,0 +1,188 @@ +# Codestyle памятки для написания более читабельного и лаконичного кода ✏️ + +Существует множество принципов, методов и лучших практик для написания чистого кода на Python. Ниже приведены несколько советов, которые помогут Вам начать работу и упростить процесс написания + +### Переменные + ++ Имена переменных должны быть snake_case и в нижнем регистре `first_name` + ++ Используйте информативные описательные имена, которые легко читаются. + + ```python + # Не рекомендуется + au = 105 + + # Рекомендуется + active_users = 105 + ``` + ++ Имена констант должны быть snake_case и в верхнем регистре `LAST_NUMBER` + ++ Не используйте магические числа. + ```python + # Не рекомендуется + def roll_dice(): + return random.randint(0, 4) # what is 4 supposed to represent? + + # Рекомендуется + DICE_SIDES = 4 + + def roll_dice(): + return random.randint(0, DICE_SIDES) + ``` + +### Функции + ++ Имена функций должны быть snake_case и в нижнем регистре `(quick_sort())`; + ++ Вы должны подробно описывать, но только релевантную информацию. Например, хорошие имена функций описывают то, что они делают хорошо, не включая подробности +```python +DICE_SIDES = 4 + +# Не рекомендуется +def roll_dice_using_randint(): + return random.randint(0, DICE_SIDES) + +# Рекомендуется +def roll_dice(): + return random.randint(0, DICE_SIDES) +``` +### Использование пустого пространства + + Максимальная длина строки. Постарайтесь ограничить свои строки примерно 79 символами, что является рекомендацией, приведенной в руководстве по стилю [PEP 8](https://pythonworld.ru/osnovy/pep-8-rukovodstvo-po-napisaniyu-koda-na-python.html?ysclid=lmajzjrf9x736916353#section-5) + +### Аннотация типов + +Чтобы держать типизацию под контролем — применяют аннотации типов данных `Type Hints`. Это явное указание типа ожидаемых данных при объявлении переменных, классов и функций. + +Чтобы указать тип переменной в Python, просто добавьте двоеточие с пробелом, за которым следует тип (str, int, List[] и т. д.), сразу после имени переменной. + +```python +from typing import List + +name: str = 'Tommy' +age: int = 24 +height_in_meters: float = 1.7 +colleagues: List[str] = ['Alicia', 'John'] +``` + +В функциях указывают Type Hints для аргументов и возвращаемого значения , мы добавляем стрелку –>, за которой следует его тип.: + +```python +from typing import Dict, List + + +def convert_celcius_to_fahrenheit(celcius_temp: float) –> float: + return (celcius_temp * 9/5) + 32 + +def send_email(subject: str, body: str, recipients: List[str], cache: Dict[str,str])–> bool: + pass +``` + +### Docstring: документирование кода + + Рекомендации к оформлению комментариев: +* начинайте комментарий со знака `#`; +* первое слово пишите с заглавной буквы; +* следите за длиной строки — она должна быть не больше 72 символов; +* заканчивайте комментарий точкой. + +Используйте комментарии, когда нужно: + ++ указать на участок кода, на который стоит обратить внимание; ++ пояснить сложные алгоритмы или логику; ++ указать на код, который нужно доработать; ++ указать на код, который хочется позже разобрать. + +### Основные правила оформления докстрингов + ++ После открывающих тройных кавычек `"""` не должно быть пробела. +Докстринг начинается с заглавной буквы и заканчивается точкой: + + ```python + """Возвращает число 1.""" + ``` ++ Если докстринг не умещается в одну строку — можно разбить его на несколько строк. Отступы на новой строке должны выровнять текст по кавычкам: + + ```python + def tricky_func(): + """Можно перенести + так. + """ + + def muddy_func(): + """ + А можно - + так. + """ + ``` ++ Если документируется класс — после строки документации оставляется пустая строка. В остальных случаях код должен начинаться сразу после докстринга. + +Также о правилах оформления докстрингов можно ознакомиться подробнее в стандарте [PEP257](https://peps.python.org/pep-0257/). + +## Django codestyle + +### F строка ++ f-строки должны использовать только простой доступ к переменным и свойствам с предварительным назначением локальной переменной для более сложных случаев: + +```python +# Разрешено +f'hello {user}' +f'hello {user.name}' +f'hello {self.user.name}' + +# Не разрешено +f'hello {get_user()}' +f'you are {user.age * 365.25} days old' +``` + +### Импорты ++ В каждой строке расположите элементы импорта в алфавитном порядке с элементами в верхнем регистре, перед элементами в нижнем регистре. + ++ Разорвите длинные строки скобками и сделайте отступ на 4 пробела. Включите запятую после последнего импорта и поместите закрывающую круглую скобку в отдельную строку. + ++ Используйте одну пустую строку между последним импортом и любым кодом уровня модуля и используйте две пустые строки над первой функцией или классом. + ++ Например (комментарии предназначены только для пояснительных целей): +``` python +django / contrib / admin / example.py ¶ +# future +from __future__ import unicode_literals + +# standard library +import json +from itertools import chain + +# third-party +import bcrypt + +# Django +from django.http import Http404 +from django.http.response import ( + Http404, HttpResponse, HttpResponseNotAllowed, StreamingHttpResponse, + cookie, +) + +# local Django +from .models import LogEntry + +# try/except +try: + import yaml +except ImportError: + yaml = None + +CONSTANT = 'foo' + + +class Example: + # ... +``` ++ По возможности используйте удобный импорт. Например, сделайте так: +```python +from django.views import View +``` +вместо: +```python +from django.views.generic.base import View +``` diff --git a/docs/diagram_video_workers.bpmn b/docs/diagram_video_workers.bpmn new file mode 100644 index 00000000..27ddac37 --- /dev/null +++ b/docs/diagram_video_workers.bpmn @@ -0,0 +1,239 @@ + + + + + + Flow_1efu2yr + Flow_0tolboa + Flow_05n7y75 + + + Flow_12crdx9 + Flow_135pjiu + Flow_1efu2yr + Flow_0dwt5en + + + DataStoreReference_18q7jt9 + Property_16v7xos + + + + Flow_135pjiu + Flow_1e4jyds + + + + Flow_05n7y75 + Flow_0dhx0lt + + + + Flow_0dhx0lt + Flow_0pxo7rr + + + + Flow_0fn82am + Flow_02w5v9q + Flow_0paptk7 + + + Flow_12crdx9 + + + Flow_0dwt5en + Flow_0tolboa + + + + Flow_02w5v9q + Flow_1ipc6b3 + + + + Flow_0paptk7 + Flow_1ipc6b3 + Flow_1e4jyds + + + + + + + + + Flow_0pxo7rr + Flow_0fn82am + + + + + видео есть + + + видео нет + + + места нет + + + место есть + + + видео нет + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/diagram_video_workers.bpmn.svg b/docs/diagram_video_workers.bpmn.svg new file mode 100644 index 00000000..896a068a --- /dev/null +++ b/docs/diagram_video_workers.bpmn.svg @@ -0,0 +1,4 @@ + + + +проверить есть ли достаточно места на дискедобавить на дискПроверка наличия видео игры с игрокомна дискезапрос от менеджеровПередача данных в брокер сообщенийпоставить в приоритет в брокеревидео обработаноработа на стороне дс-ов???? Как они будут возвращать данные??Передавать данные на сервис по обработке видео. На странице игрока отображать статус процесса.очистить необходимое место. Удалять либо по дате добавления либо по кол-ву запросов к видео.отображать на странице игрока ссылку для скачиваниявидеоПоявление записи о игре в бдместа нетместо естьвидео нетвидео естьвидео нет diff --git a/docs/materials/instructions.md b/docs/materials/instructions.md new file mode 100644 index 00000000..6434abec --- /dev/null +++ b/docs/materials/instructions.md @@ -0,0 +1,60 @@ +# Инструкции + +### Общие правила коммуникации на проекте + +1. Рабочее время: распределение времени с пн-вс для студентов, пн-пт для PM и TL, но для решения важных вопросов можно связаться и в выходные: + + PM - с 12:00 до 22.00 по мск; + TL Константин Райхерт - с 10.00 до 23.00 по мск; + + - Работаем недельными спринтами (далее по скорости разработки). + - Стендапы понедельник, среда, пятница + - Еженедельные встречи с тимлидами один на один. + - В середине спринта (вт-чт) груминг задач для формирования бэклога на следующий спринт. + +2. Каналы коммуникации: + - пачка **Федерация_адаптивного_хоккея_general** - анонсы и stand up + + +### Ритуалы + +1. **Письменный стендап** + +Когда: понедельник, среда, пятница до 19:00 +Где: в тредах к сообщению в пачке +Цель: собрать актуальную информацию о положении дел в команде, выявить направления, требующие проработки тимлидами/техлидами/кураторами +Как: написать сообщение по установленному формату в треде в пачке. (ПМ создает тред с утра) + +2. **Встреча one-to-one** + +Когда: по необходимости +Где: по видео, один на один с тимлидом +Цель: решить вопросы вызывающие стопор, обменяться опытом. +Как: участник команды договаривается о созвоне лично с тимлидом. + +3. **Ретро** + +Когда: по итогам спринта +Где: по видео +Цель: улучшение командных процессов за счет обсуждения предыдущих событий и процессов в команде, которые наблюдались в течение спринта +Как: вместе анализируем спринт и отвечаем на вопросы: + +- Плюсы. Что шло хорошо в прошлой итерации? +- Минусы. Какие проблемы были в прошлой итерации? +- Идеи. Какие идеи появились по ходу ретроспективы? +- План. Какие улучшения мы запланируем на следующую итерацию? +(если необходимо, закрепить ответственного за конкретные события) + +4. **Организация встреч в Zoom** + + - Описание встречи + - Цели + - Фиксированный тайминг + - Запись при необходимости + +5. **Стендап (в письменном формате)** +- Что делал вчера: +- Что буду делать сегодня: +- Какие есть вопросы/проблемы (тегай того, с кем конкретно хочешь переговорить): + *Если вопросов/проблем нет, так и пишешь, что их нет* +- Предложения/пожелания: diff --git a/docs/specification_of_analysts.md b/docs/specification_of_analysts.md new file mode 100644 index 00000000..7fa1b57e --- /dev/null +++ b/docs/specification_of_analysts.md @@ -0,0 +1,492 @@ +1. **Введение:** + +Спецификация требований к сайту базы данных адаптивного хоккея с данными игроков и команд для интеграции дополнительных данных по видеоряду с игр соревнований + +**1.1 Цель** +Обновленная версия сайта даст возможность пользователям просматривать видео игр с командами и просматривать отрезки видео игры с конкретным игроком в кадре, что позволит сократить время поиска видео игр и соревнований и создаст базу с видео данными об игроках и командах. + +**1.2. Границы проекта** + +* создание базы игр с видео для каждого игрока +* Скачивание видео конкретного игрока в его карточке +* доступ к поиску видео с играми из единого источника +* синхронизация игр по датам + +2. **Общее описание:** + + +**2.1. Контекстная диаграмма, общий взгляд на продукт** + +**2.2. Классы пользователей и их характеристики:** + +| Класс пользователя | Описание | +| ----- | ----- | +| Администратор | добавляет игроков; вносит и редактирует любые данные в БД; добавляет ссылку на видео новой игры/соревнования просматривает видео всех игр и видеоряд с участием конкретного игрока (всех существующих игроков) | +| Модератор | вносит данные по определенному ребенку; не может просматривать базу данных полностью; для обращения к базе данных может использовать только поисковую строку, из которой осуществляется поиск по ФИО игрока. по результатам поискового запроса, может выбрать карточку игрока для ее просмотра и редактирования. | +| Представитель команды | просматривает и редактирует данные игроков своей команды; ограничение \- просматривает только следующие данные по игрокам в других командах: ФИО, возраст спортивный класс, тип заболевания просматривает видео игры/соревнования своей команды просматривает видеоряд с игр/соревнований конкретного игрока своей команды | + +**2.3. Предположения и зависимости:** + +- Пре-1: скачивание видеоряда с участником на сайте должно быть доступно всегда, когда работает сайт и сервис обработки видео; +- Зав-1: функциональность формирования видеоряда с участником на сайте зависит от наличия ссылки на видеозапись игры и результатов обработки видео внешним сервисом, которые хранятся в базе данных; +- Зав-2: функциональность формирования из видео таймслотов с участником на сайте зависит от наличия ссылки на видеозапись игры и результатов обработки видео внешним сервисом, которые хранятся в базе данных. + + +**2.4 Ограничения проектирования и реализации** + Огр \- 1 Сервис принимает и обрабатывает ссылки на видео только с “Яндекс Видео” + Огр \- 2 Сервис возвращает видео с игроком. Не хранит обработанное видео. + +3. **Функции системы:** + + +**3.1. Добавление ссылки на видеоконтент** +**3.1.1. Описание** +Авторизованный пользователь (администратор) Сайта Федерации адаптивного хоккея может добавлять видео на игру, а также информацию о командах, принявших участие в игре, игроках и их игровых позициях, чтобы запустить процесс обработки видео сервисом. + +**3.1.2. Use Case** +![][image1] + +| Уникальный код и название | ВИ-1: Добавить ссылку на видео игры | +| ----- | ----- | +| Контекст использования | Добавить ссылку на видео с новой игрой/соревнованием команды | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор | +| Предусловие | Пользователь авторизовался на сайте | +| Ограничение | Невозможно за раз добавить несколько ссылок, их нужно добавлять последовательно из\-за ограничений сервиса распознавания видео | +| Гарантии успеха | Ссылка добавлена Создана запись с уникальным id | +| Триггер | Администратор хочет добавить новое видео игры со своей командой на сайт | +| Входные данные | ссылка на видео название команд-участниц из БД id команд и id игроков ФИО игроков команд игровые позиции (номера) спортсменов цвет формы каждой команды | +| Результат | в БД создана запись | +| Базовый сценарий | Добавить ссылку на видео Пользователь выбирает форму ввода данных для добавления ссылки на видео Сайт отображает форму ввода данных с полями к заполнению Пользователь Заполняет необходимую информацию: ссылка на видео Сайт отображает информацию о том, что ссылка на новое видео добавлена | +| Расширения | Ссылка не добавлена 4.а.1. Сайт отображает информацию о том, что ссылка не добавлена (подсвечивает поле необходимое к заполнению) 4.a.2. Сайт возвращает на шаг 3 основного сценария 4.b.1. Сайт отображает информацию о том, что указанная ссылка существует 4.b.2. Сайт возвращает на шаг 3 основного сценария 4.c.1. Сайт отображает ошибку формата ввода данных о ссылке. 4.c.2. Сайт возвращает на шаг 3 основного сценария | +| Ошибки | Логическая \- дубль ссылки Формат данных ссылки Синхронизация с API сервиса: один из вызовов к API сервиса по обработке видео завершился неудачно \- сайт отображает информацию о том, что добавить ссылку не удалось (например, невалидность данных) | +| Изменения в технологии и данных | Использовать сервис по валидации ссылок | + +| Уникальный код и название | ВИ-1.1: Заполнить данные команды | +| ----- | ----- | +| Контекст использования | Внесение данных команды для описания видео | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор | +| Предусловие | Пользователь авторизовался на сайте и начал заполнять форму для ввода данных о ссылке на видео | +| Гарантии успеха | Внесены данные о двух командах-участниках | +| Триггер | Пользователь хочет указать названия команд-участников игры, чтобы добавить новую ссылку с видео игры на сайт | +| Входные данные | названия команд из БД, принимающие участие в игре цвет формы каждой команды | +| Результат | данные команд указаны в записи со ссылкой на игру и учтены в JSON-объекте | +| Базовый сценарий | Добавить название команд-участников соревнования Пользователь выбирает в форме ввода данных для добавления ссылки на видео названия команд из выпадающего списка автоподстановки Сайт отображает в форме ввода данных в поле с названием команды выпадающий список существующих в БД команд Пользователь выбирает необходимые команды Сайт отображает информацию о том, что информация о командах-участниках добавлена | +| Расширения | Название команд не добавлены 1а. В выпадающем перечне автозаполнения отсутствует нужная команда 1b. Сайт предлагает добавить новую команду 1с. Пользователь выбирает добавить новую команду нажатием кнопки “+ добавить команду” 1d. Сайт переходит на шаг 1 сценария “Добавить команду” 1e. Пользователь следует шагам сценария “добавить команду” 1f. Сайт возвращается на шаг 1 основного сценария после успешного добавления новой команды 1f1. Сай возвращается на шаг 1 основного сценария после неуспешного добавления новой команды 1g. Сайт переходит на шаг 2 основного сценария 4а. Сайт отображает информацию о том, что информация не добавлена 4b. Сайт переходит на шаг 3 основного сценария | +| Ошибки | Логические \- дубликаты Нет нужной команды в БД | +| Изменения в технологии и данных | Использовать справочник команд с возможностью поиска Добавить кнопку выбора команд из справочника | + +| Уникальный код и название | ВИ-1.2: Заполнить данные спортсменов | +| ----- | ----- | +| Контекст использования | Внесение данных состава команды для описания видео | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор | +| Предусловие | Пользователь авторизовался на сайте и начал заполнять форму для ввода данных о ссылке на видео | +| Гарантии успеха | Внесены данные о спортсменах команды, которые участвовали в игре | +| Триггер | Пользователь хочет указать ФИО и игровые позиции спортсменов команды, которые приняли участие в игре, чтобы добавить новое видео игры на сайт | +| Входные данные | ФИО спортсмена игровая позиция спортсмена | +| Результат | данные спортсмена и его игровой позиции указаны в записи со ссылкой на игру и учтены в JSON-объекте | +| Базовый сценарий | Добавить ФИО и игровой номер спортсменов своей команды Пользователь выбирает в форме ввода данных для добавления ссылки на видео спортсменов-участников игры из выпадающего списка автозаполнения Пользователь проверяет игровые позиции игроков Сайт отображает в форме ввода данных в поле с ФИО игроков команды и их игровые позиции Пользователь подтверждает корректность указанных данных Сайт отображает информацию о том, что информация о спортсменах добавлена | +| Расширения | ФИО спортсменов не добавлены 1а. Необходимы игрок отсутствует 1b. Сайт предлагает добавить нового игрока 1с. Пользователь выбирает добавить нового игрока нажатием кнопки “+ добавить игрока” 1d. Система переходит на шаг1 сценария “добавить игрока” 1e. Пользователь следует шагам сценария “добавить игрока” 1f. Сайт возвращается на шаг 1 основного сценария после успешного добавления нового игрока 1f1. Сай возвращается на шаг 1 основного сценария после неуспешного добавления нового игрока 1g. Сайт переходит на шаг 2 основного сценария 2а. Пользователь корректирует игровые позиции игроков, когда номера в игре отличаются от номеров в БД или отсутствуют в БД 4а. Сайт отображает информацию о том, что информация не добавлена 4b. Сайт переходит на шаг 3 основного сценария | +| Ошибки | Логические \- дубли Нет нужного игрока в БД Не указана игровая позиция игрока | +| Изменения в технологии и данных | Использовать справочник спортсменов с возможностью поиска Добавить кнопку выбора ФИО из справочника | + +| Уникальный код и название | ВИ-2: Посмотреть карточку спортсмена | +| ----- | ----- | +| Контекст использования | Просмотр карточки спортсмена для получения информации о том, в каких играх он принимал участие, и о возможности посмотреть видеоряд с игроком | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Представитель команды, Администратор | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти спортсмена” | +| Гарантии успеха | Карточка спортсмена просмотрена | +| Триггер | Пользователь хочет посмотреть карточку игрока, чтобы увидеть, в каких играх он принимал участие, и видеоряд моментов с ним | +| Входные данные | ФИО интересующего игрока | +| Результат | в карточке спортсмена отображена информация об играх, в которых он принял участие в карточке спортсмена отражена ссылка на видеоряд с игроком с игры, в которой он принял участие, если ссылка на игру с участием выбранного игрока есть в БД | +| Базовый сценарий | Открыть карточку спортсмена Пользователь выбирает интересующего игрока Система отображает карточку выбранного игрока Пользователь просматривает информацию об игроке, в том числе видит данные об играх, где спортсмен принял участие, и данные о видеорядах со спортсменом, доступных к просмотру | +| Расширения | 1а. Пользователь осуществляет поиск спортсмена со стартовой страницы 1b. Система переходит на шаг 2 основного сценария 1b1а. Отсутствует результат поиска 1b1b. Система отображает соответствующее сообщение 1b1c. Система переходит на стартовую страницу сайта 4а. В карточке спортсмена отсутствует информация об играх, в которых он принял участие 4b. В карточке спортсмена отсутствуют данные о видеорядах, доступных к просмотру | +| Изменения в технологии и данных | Использовать справочник спортсменов Использовать справочник игр | + +| Уникальный код и название | ВИ-3: Посмотреть карточку команды | +| ----- | ----- | +| Контекст использования | Просмотр карточки команды, чтобы видеть информацию об играх, в которых команда приняла участие | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Представитель команды, Администратор | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти команду” | +| Гарантии успеха | Карточка команды открыта | +| Триггер | Пользователь хочет открыть карточку команды, чтобы увидеть информация об играх, где команда участвовала, состав команды в играх и видеоряды с игроками | +| Входные данные | название команды | +| Результат | в карточке команды отображена информация об играх, в которых она участвовала в карточке команды отражена ссылка на видеоряд с игроками с игр, в которой они приняли участие, если ссылки на игры с участием выбранной команды есть в БД | +| Базовый сценарий | Открыть карточку команды Пользователь выбирает интересующую Команду Система отображает данные карточки команды Пользователь просматривает данные о команде, в том числе видит информацию об играх, в которых команда приняла участие, и информацию о видеорядах с игр, доступных к просмотру, которые содержат кадры с участниками команды | +| Расширения | 1а. Интересующая команда отсутствует в перечне 3а. В карточке отсутствует информация о видео с игр 3b. В карточке отсутствует информация о видеорядах со спортсменами | +| Изменения в технологии и данных | Использовать справочник команд Использовать справочник игр | + +**3.1.3. Функциональные требования** + +| Ссылка.Добавить | Добавить ссылку на видео игры | +| :---- | :---- | +| .Добавить | Сайт БД должен предоставлять возможность добавления ссылки на видео игры/соревнования | +| .ВнесениеДанных | Сайт БД должен предоставлять возможность внесения данных команды и игроков | +| .Ошибка: | Сайт БД должен уведомлять пользователя об ошибке добавления новой ссылки | +| .ЗапросТрекинг | Сайт БД должен отправлять запрос на трекинг на сервис по обработке видео в JSON-файле | +| .УведомитьПользователя | Сайт БД должен отправлять на электронную почту пользователя уведомление об окончании обработки после того, как в БД поступил результат обработки видео от Сервиса | +| .ОтветТрекинг | Сайт БД должен принимать ответ от сервиса по обработке данных и хранить его в JSON-файле | +| Ссылка.Просматривать | Видеть ссылки на видеоматериалы в карточках игрока и команды | +| .КарточкаСпортсмен | Сайт БД должен отражать ссылку на игру, в которой принял участие конкретный спортсмен, в его карточке | +| .КарточкаКоманда | Сайт БД должен отражать ссылку на игру, в которой принял участие конкретная команда, в ее карточке | + +**3.2. Просмотр видеоряда с игр и соревнований с игроками в кадре.** + +**3.2.1. Описание** +Авторизованный пользователь Сайта может скачать видеоряд с игр и соревнований из моментов, где спортсмен присутствует в кадре, на интересующую дату. + +**3.2.2. Use Case** +![][image2] + +| Уникальный код и название | ВИ-1: Запросить видеоряд со спортсменом | +| ----- | ----- | +| Контекст использования | Запросить создание видеоряда из эпизодов со спортсменом с интересующей пользователя игры | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Посмотреть карточку спортсмена” | +| Гарантии успеха | Видеоряд с кадрами конкретного игрока в конкретной игре готов к скачиванию | +| Триггер | Пользователь хочет запросить видео с определенной игры, в которой принял участие определенный игрок | +| Входные данные | JSON-объект | +| Результат | в БД создана запись о склейке видеоряда по конкретному игроку (ссылка на видео+ФИО конкретного игрока \+ игровая позиция игрока \+ результаты трекинга по игрокам и временным промежуткам с учетом рекламы из БД) запись упакована в JSON-объект объект передан на сервис по обработке видео от сервиса по обработке видео получен результат склейки видеоряда по конкретному игроку в виде видеофайла | +| Базовый сценарий | Запросить видеоряд Пользователь в карточке спортсмена нажимает на кнопку “скачать видео с игроком” Система выводит сообщение о запуске процесса создания видео Система выводит сообщение о готовности видео | +| Расширения | 1а. В карточке спортсмена отсутствует ссылка на скачивание видеоряда с игроком 1b1. Система предлагает добавить видео со спортсменом с последующим переходом в форму добавления видео 1b2. Система отображает сообщение в блоке с расположением ссылок на видеоряды о том, что видео с игроком может добавить Администратор и необходимость обратиться к нему при наличии ссылки на видео с игры, где участвует конкретный спортсмен 2а. Система выводит сообщение о сбое процесса генерации видео 2b. Система переходит на шаг 1 основного сценария 3а. Система не выводит сообщение о готовности видео более 2 часов 3b. Система выводит сообщение об ошибке в процессе генерации видео 3с. Система переходит на шаг 1 основного сценария | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть кнопку “скачать видео для просмотра” | + +| Уникальный код и название | ВИ-2: Скачать видеоряд со спортсменом | +| ----- | ----- | +| Контекст использования | Скачать видеоряд из эпизодов с конкретным спортсменом с интересующей пользователя игры | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Запросить видеоряд со спортсменом” | +| Гарантии успеха | Видеофайл с видеорядом с кадрами конкретного игрока в конкретной игре скачан | +| Триггер | Пользователь хочет скачать видеоряд с определенной игры, в которой принял участие определенный игрок | +| Входные данные | видеофайл от сервиса по обработке видео со склейкой кадров по конкретному игроку, который принял участие в интересующей игре | +| Результат | устройство пользователя сохранило видеофайл и воспроизводит его без ошибок | +| Базовый сценарий | Скачать видеоряд В карточке спортсмена кнопка с символом скачивания файла активна Пользователь нажимает на кнопку с символом скачивания файла Система начинает процесс передачи данных на устройство пользователя Система выводит сообщение о том, что файл загружен, после полного скачивания файла на устройство пользователя Пользователь запускает скачанный файл на своем устройстве и просматривает его | +| Расширения | 1а. Кнопка с символом скачивания файла не активна 1b. Система выводит сообщение об ошибке при формирования файла для скачивания 1с. Система переходит на шаг 1 основного сценария 3а. Система не начинает процесс передачи данных и выводит сообщение об ошибке (соединения, функциональности браузера) 3b. Система переходит на шаг 1 основного сценария 4а. Система выводит сообщение об ошибке в процессе скачивания файла с видео 4b. Система переходит на шаг 1 основного сценария 5а. Пользователь не может запустить видеофайл на своем устройстве из\-за неисправности файла 5b. Пользователь переходит на шаг 1 основного сценария | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть кнопку с символом скачивания | + +| Уникальный код и название | ВИ-3: Отфильтровать ссылки на видеоряд в карточке игрока | +| ----- | ----- | +| Контекст использования | Посмотреть ссылки для скачивания на видеоряд игры или соревнований, в которых участвовал игрок, с учетом интересующей даты. | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Посмотреть карточку спортсмена” | +| Гарантии успеха | Ссылки на видеоряды найдены Ссылки на видеоряды не найдены | +| Триггер | Пользователь хочет посмотреть ссылки на видеоряды с игроком в карточке спортсмена за определенный период | +| Входные данные | карточка спортсмена | +| Результат | пользователь видит в карточке игрока все существующие видеоряды с ним | +| Базовый сценарий | Фильтровать ссылки на видеоряд с игроком на заданную дату Пользователь в карточке конкретного игрока в разделе со ссылками на видеоряды с его участием в играх выбирает временной промежуток для игры в формате “с” \- “по” из выпадающей формы “календарь” Система отображает пользователю игры и ссылки на видеоряды с конкретным спортсменом найденные ссылки Пользователь просматривает ссылки | +| Расширения | 2а. Система не отображает ссылки, что означает, что за выбранный Пользователем период спортсмен не принимал участие ни в одной игре 2b. Система отображает игры, но не отображает ссылки. Это означает, что видеоряд на игрока не создан. | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть форму автозаполнения “календарь” | + +**3.2.3. Функциональные требования** + +| Видео.Запросить | Запрос на скачивание видео | +| :---- | :---- | +| .ОтправитьЗапросНаСервис | Система должна предоставлять пользователю возможность отправить запрос на сервис по обработке видео по формированию видеоряда из кадров с игроком | +| .ОшибкаЗапроса | Система должна уведомлять пользователя, когда процесс по формированию видео неуспешен | +| Видео.Скачать | Скачивание видеоролика | +| .СкачатьФайл | Система должна предоставлять пользователю скачать сформированный видеоряд с интересующим игроком | +| .ОшибкаСкачивания | Система должна уведомлять пользователя при ошибке скачивания файла | +| Карточка.Игрока | Работать с карточкой игрока | +| .ВидеоФильтровать | Система должна позволять пользователю фильтровать ссылки на видеоряды с игроком в его карточке на выбранную дату | + +**3.3 Поиск ссылок на видео** + +**3.3.1. Описание** +Авторизованный пользователь Сайта может искать ссылки на видео игр/соревнования в разделе Сайта “Игра” по игре, команде и игроку, фильтровать и сортировать результаты поиска. + +**3.3.2. Use Case** +![][image3] + +| Уникальный код и название | ВИ-1: Найти видеоматериалы по игроку | +| ----- | ----- | +| Контекст использования | Найти ссылки на видеоряд со спортсменом по ФИО | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Ссылка на видеоряд со спортсменом найдена Ссылка на видеоряд со спортсменом не найдена | +| Триггер | Пользователь хочет найти все видеоряды по конкретному игроку | +| Базовый сценарий | Поиск видеорядов с игроком по ФИО игрока Пользователь перешел в раздел Сайта БД “Игры” Система отображает полный список всех существующих игр Пользователь вводит в форму поиска ФИО интересующего спортсмена и выбирает из выпадающего перечня нужного игрока Система отображает все ссылки на видеоряды с игроком | +| Расширения | 3а. Пользователь вводит не буквенные значения 3а1\. Интересующий игрок отсутствует в базе данных 3b. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных 4а. Отсутствуют ссылки на видеоряды с интересующим игроком | +| Изменения в технологии и данных | Использовать справочник игр Использовать справочник игроков Предусмотреть автозаполнение данных по ФИО игрока | + +| Уникальный код и название | ВИ-2: Фильтровать видеоматериалы с игроком | +| ----- | ----- | +| Контекст использования | Отфильтровать видео со спортсменом по интересующему промежутку времени (по датам) | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти видеоматериалы по игроку” | +| Гарантии успеха | Ссылки на видеоряд найдены Ссылки на видеоряд не найдены | +| Триггер | Пользователь хочет выбрать ссылки на видеофайлы по интересующему игроку за определенный период времени | +| Базовый сценарий | Фильтровать выборку ссылок на видеофайлы Пользователь нажимает на кнопку ввода данных для фильтрации по дате Пользователь заполняет период дат, за который ему нужно увидеть ссылки на видео, из выпадающей формы автозаполнения “календарь” Система отображает ссылки на видеоряды за указанный период Пользователь просматривает ссылки на видеофайлы | +| Расширения | 2а. Пользователь вводит нечисловые значения 2b. Система выдает ошибку в формате ввода данных 2с. Система возвращается на шаг 1 основного сценария 3а. Система не отображает ссылки на видеоряд с участником 3b. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть возможность открытия “календаря” при вводе данных по датам для фильтрации Предусмотреть кнопку “сбросить фильтр” | + +| Уникальный код и название | ВИ-3: Сортировать видеоматериалы с игроком | +| ----- | ----- | +| Контекст использования | Сортировать видео со спортсменом в хронологическом порядке | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти видеоматериалы по игроку” | +| Гарантии успеха | Ссылки на видеоряд отсортированы по убыванию Ссылки на видеоряд отсортированы по возрастанию | +| Триггер | Пользователь хочет сортировать ссылки на видеофайлы по интересующему игроку в хронологическом порядке | +| Базовый сценарий | Сортировать выборку ссылок на видеоряды интересующего спортсмена Пользователь нажимает на кнопку с символом “сортировать по дате” Система выводит записи со ссылками на видеофайлы по интересующему игроку в хронологическом порядке “от новых к старым” Пользователь просматривает ссылки на видеоряды | +| Расширения | 1а. Пользователь повторно нажимает на кнопку с символом “сортировать по дате” 1b. Система выводит записи со ссылками на видеофайлы по интересующему игроку в хронологическом порядке “от старых к новым” | +| Изменения в технологии и данных | Использовать справочник игр | + +| Уникальный код и название | ВИ-4: Найти видеоматериалы по игре | +| ----- | ----- | +| Контекст использования | Найти ссылки на видеозапись игры | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Ссылка на видео с игрой найдена Ссылка на видео с игрой не найдена | +| Триггер | Пользователь хочет найти видео по конкретной игре | +| Базовый сценарий | Поиск видео по интересующей игре Пользователь перешел в раздел Сайта БД “Игры” Система отображает полный список всех существующих ссылок на видео с игр Пользователь вводит в форму поиска название интересующей игры Система отображает ссылку на видео с игрой | +| Расширения | 3а. Пользователь вводит не буквенные значения 3b. Интересующая игра отсутствует в базе данных 3c. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных 4а. Отсутствует ссылка на видео по интересующей игре | +| Изменения в технологии и данных | Использовать справочник игр | + +| Уникальный код и название | ВИ-5: Фильтровать видеоматериалы по играм | +| ----- | ----- | +| Контекст использования | Отфильтровать ссылки на видео игр по интересующему промежутку времени (по датам) | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Ссылки на видео найдены Ссылки на видео не найдены | +| Триггер | Пользователь хочет выбрать ссылки на видео по играм за определенный период времени | +| Базовый сценарий | Фильтровать ссылки на видео по играм Пользователь нажимает на кнопку ввода данных для фильтрации по дате Пользователь заполняет период дат, за который ему нужно увидеть ссылки на видео, из выпадающей формы автозаполнения “календарь” Система отображает ссылки на видеоряды за указанный период Пользователь просматривает ссылки на видео | +| Расширения | 2а. Пользователь вводит нечисловые значения 2b. Система выдает ошибку в формате ввода данных 2с. Система возвращается на шаг 1 основного сценария 3а. Система не отображает ссылки на видео игр 3b. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть возможность открытия “календаря” при вводе данных по датам для фильтрации Предусмотреть кнопку “сбросить фильтр” | + +| Уникальный код и название | ВИ-6: Сортировать видеоматериалы по играм | +| ----- | ----- | +| Контекст использования | Сортировать видео с игр в хронологическом порядке | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Ссылки на видео отсортированы по убыванию Ссылки на видео отсортированы по возрастанию | +| Триггер | Пользователь хочет сортировать ссылки на видео по играм в хронологическом порядке | +| Базовый сценарий | Сортировать список ссылок на видео игр Пользователь нажимает на кнопку с символом “сортировать по дате” Система выводит записи со ссылками на видео по играм в хронологическом порядке “от новых к старым” Пользователь просматривает ссылки на видеоряды | +| Расширения | 1a. Пользователь повторно нажимает на кнопку с символом “сортировать по дате” 1b. Система выводит записи со ссылками на видео по играм в хронологическом порядке “от старых к новым” | +| Изменения в технологии и данных | Использовать справочник игр | + +| Уникальный код и название | ВИ-7: Посмотреть видео игры на оригинальном ресурсе | +| ----- | ----- | +| Контекст использования | Посмотреть видео игры на сайте-источнике видео | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти видеоматериалы по игре” | +| Гарантии успеха | Видео игры на сайте источнике открыто и запускается | +| Триггер | Пользователь хочет посмотреть всю видеозапись игры на оригинальном ресурсе (сайт-источник видео) | +| Базовый сценарий | Смотреть видео игры: Пользователь выбирает карточку интересующей игры Пользователь нажимает на ссылку видеозаписи игры Система перенаправляет пользователя в новой вкладке на сайт-первоисточник Пользователь просматривает видеозапись игры | +| Расширения | 1а. В карточке игры отсутствует ссылка на видеозапись игры 3а. Система не переходит по ссылке \- ссылка не активна | +| Изменения в технологии и данных | Использовать справочник игр | + +| Уникальный код и название | ВИ-8: Найти видеоматериалы по команде | +| ----- | ----- | +| Контекст использования | Найти ссылки на видеоряды по командам | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Ссылки на видеоряды по командам найдено Ссылки на видеоряды по командам не найдено | +| Триггер | Пользователь хочет найти все видеоряды по конкретной команде | +| Базовый сценарий | Поиск видеорядов с командами по названию команды Пользователь перешел в раздел Сайта БД “Игры” Система отображает полный список всех существующих игр Пользователь вводит в форму поиска название интересующей команды и выбирает из выпадающего перечня нужную команду Система отображает все ссылки на видеоряды с командой | +| Расширения | 3а. Пользователь вводит не буквенные значения 3b. Интересующая команда отсутствует в базе данных 3c. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных 4а. Отсутствуют ссылки на видеоряды по интересующей команде | +| Изменения в технологии и данных | Использовать справочник игр Использовать справочник команд Предусмотреть автозаполнение данных по названию команды | + +| Уникальный код и название | ВИ-9: Фильтровать видеоматериалы с командой | +| ----- | ----- | +| Контекст использования | Отфильтровать видеоряды с игр, в которых участвовала команда, по интересующему промежутку времени (по датам) | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти видеоматериалы по команде” | +| Гарантии успеха | Ссылки на видеоряды найдены Ссылки на видеоряды не найдены | +| Триггер | Пользователь хочет выбрать ссылки на видеоряды по интересующей команде за определенный период времени | +| Базовый сценарий | Фильтровать выборку ссылок на видеофайлы Пользователь нажимает на кнопку ввода данных для фильтрации по дате Пользователь заполняет период дат, за который ему нужно увидеть ссылки на видео, из выпадающей формы автозаполнения “календарь” Система отображает ссылки на видеоряды за указанный период Пользователь просматривает ссылки на видеофайлы | +| Расширения | 2а. Пользователь вводит нечисловые значения 2b. Система выдает ошибку в формате ввода данных 2с. Система возвращается на шаг 1 основного сценария 3а. Система не отображает ссылки на видеоряд с командой 3b. Система выводит сообщение с предложением изменить/сбросить данные по фильтрации для оптимизации поиска по выборке данных | +| Изменения в технологии и данных | Использовать справочник игр Предусмотреть возможность открытия “календаря” при вводе данных по датам для фильтрации Предусмотреть кнопку “сбросить фильтр” | + +| Уникальный код и название | ВИ-10: Сортировать видеоматериалы с командой | +| ----- | ----- | +| Контекст использования | Сортировать видеоряды с игр, в которых участвовала команда, в хронологическом порядке | +| Область действия | Сайт Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Успешно выполнен базовый сценарий варианта использования “Найти видеоматериалы по команде” | +| Гарантии успеха | Ссылки на видеоряды отсортированы по убыванию Ссылки на видеоряды отсортированы по возрастанию | +| Триггер | Пользователь хочет сортировать ссылки на видеоряды по интересующей команде в хронологическом порядке | +| Базовый сценарий | Сортировать выборку ссылок на видеоряды интересующей команды Пользователь нажимает на кнопку с символом “сортировать по дате” Система выводит записи со ссылками на видеофайлы по интересующей команде в хронологическом порядке “от новых к старым” Пользователь просматривает ссылки на видеоряды | +| Расширения | 1a. Пользователь повторно нажимает на кнопку с символом “сортировать по дате” 1b. Система выводит записи со ссылками на видеофайлы по интересующей команде в хронологическом порядке “от старых к новым” | +| Изменения в технологии и данных | Использовать справочник игр | + +**3.3.3. Функциональные требования** + +| Игра.Просмотр | Просмотр информации об играх | +| :---- | :---- | +| .НайтиПоИгроку | Система должна предоставлять возможность пользователю искать игры по информации об игроках, которые приняли участие в игре | +| .ОшибкаПоискИгрок | Система должна уведомлять пользователя об ошибке поиска по ФИО игрока | +| .НайтиПоКоманде | Система должна предоставлять возможность пользователю искать игры по информации о командах, которые приняли участие в игре | +| .ОшибкаПоискКоманда | Система должна уведомлять пользователя об ошибке поиска по названию команд | +| .НайтиПоИгре | Система должна предоставлять возможность пользователю искать игры по ее названию | +| .ОшибкаПоискИгра | Система должна уведомлять пользователя об ошибке поиска по названию игры | +| .РезулататыПоискаСортировать | Система должна позволять пользователю сортировать результаты поиска по любому из параметров в хронологическом и обратном порядках | +| .РезулататыПоискаФильтровать | Система должна позволять пользователю фильтровать результаты поиска по любому из параметров на заданную дату или временной промежуток | +| .ОшибкаФильтрация | Система должна уведомлять пользователя при отсутствии данных за выбранную дату | +| .СмотретьОригинал | Система должна предоставлять возможность пользователю открыть ссылку на оригинал видео в новом окне для просмотра видео игры на источнике видео | +| .ОшибкаПросмотра | Система должна уведомить пользователя при неактивности ссылки и невозможности перейти на сайт-источник для просмотра записи игры | + +**3.4. Сервис по обработке видео** + +**3.4.1. Описание** +Сервис принимает ссылку на игру команд, распознает и отслеживает игру всех участников матча, сохраняет данные. Также, по запросу, сервис по отдельному запросу возвращает ссылку на все эпизоды с интересующим игроком для формирования видеоряда с этим спортсменом. + +**3.4.2. Use Case** +**![][image4]** + +| Уникальный код и название | ВИ-1: Распознать и отследить игру всех участников матча | +| ----- | ----- | +| Контекст использования | Обработать видео, распознать и отследить игру всех участников матча | +| Область действия | Сервис сайта Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Сервис принял запрос на обработку | +| Триггер | Пользователь хочет, чтобы сервис распознал и отследил игру всех участников матча на переданном видео | +| Базовый сценарий | Распознать видео Пользователь отправляет запрос сервису со ссылкой на источник видео и данными об участниках игры Сервис принимает запрос, загружает видеофайл. Сервис обрабатывает видео Сервис сохраняет распознанные данные по игрокам | +| Расширения | 2.а.1. Сервис не принял запрос 2.а.2. Сервис вернул пользователю сообщение об ошибке 2.b.1. Сервис не смог загрузить файл из внешнего источника 2.b.2. Сервис вернул пользователю сообщение об ошибке 3.а.1 Ошибка во время обработки видео 3.а.2 Сервис вернул пользователю сообщение об ошибке 4.а.1 Ошибка сохранения данных 4.а.2 Сервис вернул пользователю сообщение об ошибке | +| Изменения в технологии и данных | Использовать справочник игр Использовать справочник игроков | + +| Уникальный код и название | ВИ-2: Запрос на трекинг игрока | +| ----- | ----- | +| Контекст использования | Предоставить данные с трекингом требуемого игрока | +| Область действия | Сервис сайта Федерации адаптивного хоккея | +| Основное действующее лицо | Администратор, представитель команды | +| Предусловие | Пользователь авторизован на сайте | +| Гарантии успеха | Сервис принял запрос на обработку | +| Триггер | Пользователь хочет, чтобы сервис вернул ему данные с трекингом интересующего его спортсмена с определенной игры | +| Базовый сценарий | Получить трекинг Пользователь отправляет запрос сервису на получение трекинга со спортсменом с интересующей игры Сервис принимает запрос и возвращает видеоряд с игроком | +| Расширения | 2.а.1. Сервис не принял запрос 2.а.2. Сервис вернул пользователю сообщение об ошибке 2.b.1. Сервис не смог найти данные по запрашиваемому игроку 2.b.2. Сервис вернул пользователю сообщение об ошибке | +| Изменения в технологии и данных | Использовать справочник игр Использовать справочник игроков | + +**3.4.3 Функциональный требования** + +| Видео.Обработка | Обработка видео | +| :---- | ----- | +| .ЗапросНаОбработку: | Сервис должен принять POST запрос переданный сайтом с данными в формате JSON | +| .Загрузить: | Сервис должен загрузить видео из внешнего источника по ссылке полученной в запросе | +| .ОшибкаЗагрузки: | Сервис должен вернуть сообщение об ошибке, если файл не может быть загружен | +| .Конвертировать: | Сервис должен конвертировать полученный видео файл в формат обрабатываемый сервисом. | +| .ОшибкаКонвертирования: | Сервис должен вернуть сообщение об ошибке, в случае неудачного конвертирования. | +| .УдалитьРекламу: | Сервис должен найти в видео файле фреймы с рекламой запомнить их позиции и удалить. | +| .ДетектироватьИгроков: | Сервис должен детектировать и отследить всех игроков в видеофайле, а также все фреймы с этими игроками. | +| .УдалитьАртефакты: | Сервис должен очистить данные от артефактов. (Каких?) | +| .НовыеНомераИгроков: | В случае распознавания номеров игроков, которые не указаны в составе команды, сервис должен зафиксировать и вернуть эти номера для проверки. | +| .Сохранить: | Сервис должен собрать все полученные данные по результатам работы с файлом, сохранить для выдачи при повторном запросе. | + +| Видео.Трекинг | Обработка видео | +| :---- | ----- | +| .ЗапросНаТрекинг: | Сервис должен принять POST запрос переданный сайтом с данными в формате JSON | +| .ЭпизодыСИгроком: | Сервис должен вернуть видео с эпизодами с игры хоккеиста. | +| .ОшибкаДанных: | Сервис должен вернуть ошибку, если данные не доступны. | + +4. **Требования к данным** + + +**4.1. Логическая модель данных** +![][image5] + +**4.2. Словарь данных** + +| Элемент данных | Описание | Структура и тип данных | Длина | Значение | +| ----- | ----- | ----- | ----- | ----- | +| Игрок | Игрок хоккейной команды, которого нужно распознать на видео | \+Номер\_игрока | | | +| Номер\_игрока | Игровой номер хоккеиста | Числовое значение | 2 | | +| Команда | Информация о команде в которой играет игрок на видео | \+Название команды | | | +| Название команды | Название команды в которой играет игрок | Символьное значение | 30 | | +| Видео | Данные о видео игры | \+Адрес\_видео | | | +| Адрес\_видео | Путь к видеофайлу | Символьное значение | 2048 | | +| Видео с игроком | Данные о расположении игрока на видео | \+Номер\_игрока \+Адрес\_видео \+Фрейм Конечный фрейм Распознанный номер Длина\_эпизода Х\_верх\_лево У\_верх\_лево Х\_низ\_право У\_низ\_право | | | +| Фрейм | Начало эпизода с игроком на видео | Числовое значение | 8 | | +| Распознанный номер | Номер игрока, который распознан сервисом при парсинге видео | Числовое значение | 2 | | +| Длина\_эпизода | Длина эпизода с игроком | Символьное значение | 17 | чч:мм:сс-чч:мм:сс | +| Х\_верх\_лево | Верхняя левая координата по оси Х кадра с игроком | Числовое значение | 4 | | +| У\_верх\_лево | Верхняя левая координата по оси У кадра с игроком | Числовое значение | 4 | | +| Х\_низ\_право | Нижняя правая координата по оси Х кадра с игроком | Числовое значение | 4 | | +| У\_низ\_право | Нижняя правая координата по оси У кадра с игроком | Числовое значение | 4 | | + +**4.3. Отчеты** +Не предусмотрены + +**4.4 Получение, целостность, хранение и утилизация данных:** +ПД-1 Фреймы и таймслоты должны храниться на протяжении существования команд и игроков + +5. **Требования к внешним интерфейсам** + + +**5.1. Пользовательские интерфейсы** +ПИ-1: Переход к скачиванию видеоряда с игроком и таймслотов с игроком должен производиться из карточки игрока, из карточки команды, к которой прикреплен игрок, из раздела “Игра” +ПИ-2: На Сайте БД должна быть возможна сортировка всех видео по дате игры +ПИ-3: На сайте БД должен быть возможен выбор фильтрации всех видео по команде и игроку +ПИ-4: В форме Внесения новой ссылки на игру должна быть возможность заполнить данными игрока, его игровой позиции и команды, к которой он принадлежит +ПИ-5: В карточке Игры должна быть возможность перейти по ссылке и открыть оригинал видео игры на источнике +**5.2. Программные интерфейсы** +ПрИ-1: Подключаемый Сервис по обработке видео +ПрИ-1.1: Сайт БД должен передать Сервису по обработке видео ссылку на видео игры и параметры игроков, по которым нужно обработать видео +ПрИ-1.2: Подключаемый Сервис передает Сайту БД таймслоты по указанным игрокам и фреймы кадров с игроками +ПрИ-1.3: Сайт БД должен предоставить Сервису параметры игроков, по которым нужно создать видеоряд, ссылку на видео и таймслоты +ПрИ-1.4: Подключаемый Сервис передает Сайту БД готовый видеофайл \- видеоряд с кадрами конкретного игрока +ПрИ-1.5: Когда подключаемый Сервис сообщает Сайту БД, что кадры с переданными параметрами игрока отсутствуют, Сайт БД должен отобразить пользователю сообщение об отсутствии видеоряда с игроком по выбранной игре +**5.3. Аппаратные интерфейсы** +Не предусмотрены +**5.4. Коммуникационные интерфейсы** +Не предусмотрены + +6. **Атрибуты качества** + +**6.1. Удобство использования** +УИ-1: Сайт БД должен позволять Пользователю добавить ссылку на видео одной операцией +УИ-2: Сайт БД должен позволить Пользователю удалить ссылку на видео одной операцией +УИ-3: 95% новых пользователей должны успешно найти конкретную игру с первой попытки +УИ-4: Сайт БД должен позволять Пользователю скачать видеоряд с конкретным игроком одной очевидной операцией +УИ-5: Сайт БД должен позволять Пользователю сказать таймслоты с конкретным игроком одной очевидно операцией +**6.2. Производительность** +Про-1: Сайт БД должен обслуживать не менее 100 пользователей всего и не менее 30 пользователей в период пиковой активности, со средней продолжительностью сеанса 70 минут +Про-2: Все веб\-страницы, генерируемые Сайтом БД, должны полностью загружаться не более чем за 3 секунды после запроса их по интернет-подключению со скоростью 100 Мбит/сек. +Про-3: Сайт БД должен добавлять ссылку на видео игры в среднем за 1 секунду и не более чем через 6 секунд после того, как пользователь запрос системе. +Про-4: После того как пользователь вызвал скачивание видеоряда с конкретным игроком, Сайт БД должен начать запуск генерации видеоряда не позднее чем через 5 секунд. +Про-6: Ссылка для скачивания видеоряда с конкретным игроком для дальнейшего просмотра видеофайла должна стать активной в течении временного интервала от 10 минут с момент вызова скачивания Пользователем файла, и до 120 минут. +**6.3. Безопасность** +Без-1: Пользователи обязательно авторизуются для входа на Сайт БД и для выполнения всех действий на Сайте БД. +Без-2: Сайт БД должен предоставлять просмотр видео с конкретными игроками только для Участников своей команды. Доступ ко всем видео есть только у Администратора. + **6.4. Доступность** +Дос-1: Сайт БД должен быть доступен 99% времени между 3:00 и полуночью по местному времени и 95% времени между полуночью и 3:00 по местному времени, за исключением времени планового обслуживания. +**6.5. Совместимость** +Совм-1: Сайт БД должен иметь возможность запрашивать у стороннего Сервиса по обработке видео видеоряд с конкретным игроком и таймслоты по конкретному игроку. +**6.6. Целостность** +Целост-1: Сайт БД должен бессрочно хранить все данные о видео с игр и данные с видеорядом по конкретному игроку и его таймслоты в формате JSON-объекта. +**6.6. Устойчивость** +Уст-1: Если при работе Сайта БД: произошел сбой и был выполнен автоматический перезапуск Сайта БД, система должна проверять статус запроса на создание ссылки или запроса на скачивание видео, чтобы избежать дублирования данных + **6.7. Надежность** +Над-1: Количество сбоев Сайта БД при обработке запроса Пользователя на скачивание видеоряда с конкретным игроком и его таймслотов не должно превышать 10% от всех запросов в сутки. + +7. **Другие требования** + +# Приложение A: Бизнес-правила + +| ID | Определение правила | Тип правила | Статическое или динамическое | Источник | +| ----- | ----- | ----- | ----- | ----- | +| БП-1 | Видео может быть скачана только при указании его автора и источника | Ограничение | Статическое | ч.4 ГК РФ | + +[image1]: + +[image2]: + +[image3]: + +[image4]: + +[image5]: diff --git a/infra/dev/docker-compose.dev.yaml b/infra/dev/docker-compose.dev.yaml new file mode 100644 index 00000000..dd08af07 --- /dev/null +++ b/infra/dev/docker-compose.dev.yaml @@ -0,0 +1,21 @@ +#filename: docker-compose.dev.yaml +#full path: ./infra/dev/docker-compose.dev.yaml +#description: docker-compose file for development environment + +version: '3.8' +name: ahf_dev + +services: + db: + container_name: db + image: postgres:13.0-alpine + restart: always + volumes: + - postgres_db_data:/var/lib/postgresql/data/ + env_file: + - ../../.env + ports: + - 5432:5432 + +volumes: + postgres_db_data: diff --git a/infra/nginx/nginx_stage.conf b/infra/nginx/nginx_stage.conf new file mode 100644 index 00000000..bb600def --- /dev/null +++ b/infra/nginx/nginx_stage.conf @@ -0,0 +1,39 @@ +server { + listen 80; + listen [::]:80; + server_name _; + return 308 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name ${HOST}; + + include /config/nginx/ssl.conf; + + location / { + proxy_pass http://site:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /admin/ { + proxy_pass http://site:8000/admin/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /media/ { + root /var/html/; + } + + location /static/ { + root /var/html/; + } +} diff --git a/infra/prod/adaptive_hockey_federation.service b/infra/prod/adaptive_hockey_federation.service new file mode 100644 index 00000000..3c5193ff --- /dev/null +++ b/infra/prod/adaptive_hockey_federation.service @@ -0,0 +1,31 @@ +[Unit] + +Description=adaptive_hockey_federation +Requires=docker.service +After=docker.service + +[Service] + +Restart=always +RestartSec=5 +TimeOutStartSec=1200 +User=production + +WorkingDirectory=/home/production/adaptive_hockey_federation/infra/prod/ + +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env pull +ExecStartPre=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down + + +# compose up +ExecStart=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env up + +# Call when daemon allready stop +ExecStop=docker compose -f docker-compose.prod.yaml --env-file /home/production/adaptive_hockey_federation/.env down + +# Call when daemon already start +ExecStartPost=sleep 5 + +[Install] + +WantedBy=multi-user.target \ No newline at end of file diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml new file mode 100644 index 00000000..9e978a92 --- /dev/null +++ b/infra/prod/docker-compose.prod.yaml @@ -0,0 +1,57 @@ +version: '3.8' +name: ahf_prod + +services: + db: + container_name: db + image: postgres:13.0-alpine + restart: always + ports: + - 5432:${DB_PORT} + volumes: + - postgres_data:/var/lib/postgresql/data/ + env_file: + - ../../.env + + site: + image: "${IMAGE_COMPOSE}" + container_name: adaptive_hockey_federation + restart: always + volumes: + - static_value:/app/adaptive_hockey_federation/static/ + - ../../media:/app/adaptive_hockey_federation/media/ + env_file: + - ../../.env + depends_on: + - db + + swag: + image: lscr.io/linuxserver/swag:latest + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID=1002 + - PGID=1004 + - TZ=Europe/Moscow + - URL=${HOST} + - VALIDATION=http + - STAGING=${ST} + volumes: + - ../nginx/nginx_stage.conf:/config/nginx/site-confs/default.conf + - swag_volume_stage:/config + - static_value:/var/html/static/ + - ../../media:/var/html/media/ + ports: + - 443:443 + - 80:${PORT} + env_file: + - ../../.env + depends_on: + - site + restart: unless-stopped + +volumes: + static_value: + postgres_data: + swag_volume_stage: diff --git a/infra/prod/prod.Dockerfile b/infra/prod/prod.Dockerfile new file mode 100644 index 00000000..86b91607 --- /dev/null +++ b/infra/prod/prod.Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.11 + +WORKDIR /app + +COPY requirements/production.txt . +RUN pip install -r production.txt --no-cache-dir + +COPY . . + +WORKDIR /app/adaptive_hockey_federation + +CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000", "--workers", "5"] diff --git a/infra/stage/adaptive_hockey_federation.service b/infra/stage/adaptive_hockey_federation.service new file mode 100644 index 00000000..c042221b --- /dev/null +++ b/infra/stage/adaptive_hockey_federation.service @@ -0,0 +1,34 @@ +[Unit] + +Description=adaptive_hockey_federation +Requires=docker.service +After=docker.service + +[Service] + +Restart=always +RestartSec=5 +TimeOutStartSec=1200 +User=developer + +WorkingDirectory=/home/developer/adaptive_hockey_federation/infra/stage/ + +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env down +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull site +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull db +ExecStartPre=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env pull swag + + +# compose up +ExecStart=docker compose -f docker-compose.stage.yaml --env-file /home/developer/adaptive_hockey_federation/.env up + +# Call when daemon allready stop +# ExecStop= + +# Call when daemon already start +ExecStartPost= + + +[Install] + +WantedBy=multi-user.target diff --git a/infra/stage/docker-compose.stage.yaml b/infra/stage/docker-compose.stage.yaml new file mode 100644 index 00000000..dc83a6b8 --- /dev/null +++ b/infra/stage/docker-compose.stage.yaml @@ -0,0 +1,57 @@ +version: '3.8' +name: ahf_stage + +services: + db: + container_name: db + image: postgres:13.0-alpine + restart: always + ports: + - 5432:${DB_PORT} + volumes: + - postgres_data:/var/lib/postgresql/data/ + env_file: + - ../../.env + + site: + image: "${IMAGE_COMPOSE}" + container_name: adaptive_hockey_federation + restart: always + volumes: + - static_value:/app/adaptive_hockey_federation/static/ + - ../../media:/app/adaptive_hockey_federation/media/ + env_file: + - ../../.env + depends_on: + - db + + swag: + image: lscr.io/linuxserver/swag:latest + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID=1002 + - PGID=1004 + - TZ=Europe/Moscow + - URL=${HOST} + - VALIDATION=http + - STAGING=${ST} + volumes: + - ../nginx/nginx_stage.conf:/config/nginx/site-confs/default.conf + - swag_volume_stage:/config + - static_value:/var/html/static/ + - ../../media:/var/html/media/ + ports: + - 443:443 + - 80:${PORT} + env_file: + - ../../.env + depends_on: + - site + restart: unless-stopped + +volumes: + static_value: + postgres_data: + swag_volume_stage: diff --git a/infra/stage/stage.Dockerfile b/infra/stage/stage.Dockerfile new file mode 100644 index 00000000..1449131f --- /dev/null +++ b/infra/stage/stage.Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.11 + +WORKDIR /app + +COPY requirements/develop.txt . +RUN pip install -r develop.txt --no-cache-dir + +COPY . . + +WORKDIR /app/adaptive_hockey_federation + + +CMD ["gunicorn", "core.wsgi:application", "--bind", "0:8000" ] diff --git a/poetry.lock b/poetry.lock index a80d954c..cd25d711 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,28 +1,235 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "asgiref" -version = "3.7.2" +version = "3.8.1" description = "ASGI specs, helper code, and adapters" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "certifi" +version = "2024.6.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +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" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "django" -version = "4.2.6" +version = "4.2.13" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" files = [ - {file = "Django-4.2.6-py3-none-any.whl", hash = "sha256:a64d2487cdb00ad7461434320ccc38e60af9c404773a2f95ab0093b4453a3215"}, - {file = "Django-4.2.6.tar.gz", hash = "sha256:08f41f468b63335aea0d904c5729e0250300f6a1907bf293a65499496cdbc68f"}, + {file = "Django-4.2.13-py3-none-any.whl", hash = "sha256:a17fcba2aad3fc7d46fdb23215095dbbd64e6174bf4589171e732b18b07e426a"}, + {file = "Django-4.2.13.tar.gz", hash = "sha256:837e3cf1f6c31347a1396a3f6b65688f2b4bb4a11c580dcb628b5afe527b68a5"}, ] [package.dependencies] @@ -35,183 +242,2102 @@ argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] [[package]] -name = "flake8" -version = "6.1.0" -description = "the modular source code checker: pep8 pyflakes and co" +name = "django-debug-toolbar" +version = "4.4.2" +description = "A configurable set of panels that display various debug information about the current request/response." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_debug_toolbar-4.4.2-py3-none-any.whl", hash = "sha256:5d7afb2ea5f8730241e5b0735396e16cd1fd8c6b53a2f3e1e30bbab9abb23728"}, + {file = "django_debug_toolbar-4.4.2.tar.gz", hash = "sha256:9204050fcb1e4f74216c5b024bc76081451926a6303993d6c513f5e142675927"}, +] + +[package.dependencies] +django = ">=4.2.9" +sqlparse = ">=0.2" + +[[package]] +name = "django-environ" +version = "0.11.2" +description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "django-environ-0.11.2.tar.gz", hash = "sha256:f32a87aa0899894c27d4e1776fa6b477e8164ed7f6b3e410a62a6d72caaf64be"}, + {file = "django_environ-0.11.2-py2.py3-none-any.whl", hash = "sha256:0ff95ab4344bfeff693836aa978e6840abef2e2f1145adff7735892711590c05"}, +] + +[package.extras] +develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.dev0)", "pytest (>=4.6.11)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +docs = ["furo (>=2021.8.17b43,<2021.9.dev0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"] + +[[package]] +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.6" files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, ] [package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" +Django = ">=3.2" [[package]] -name = "flake8-isort" -version = "6.1.0" -description = "flake8 plugin that integrates isort ." +name = "django-phonenumber-field" +version = "7.3.0" +description = "An international phone number field for django models." optional = false python-versions = ">=3.8" files = [ - {file = "flake8-isort-6.1.0.tar.gz", hash = "sha256:d4639343bac540194c59fb1618ac2c285b3e27609f353bef6f50904d40c1643e"}, + {file = "django-phonenumber-field-7.3.0.tar.gz", hash = "sha256:f9cdb3de085f99c249328293a3b93d4e5fa440c0c8e3b99eb0d0f54748629797"}, + {file = "django_phonenumber_field-7.3.0-py3-none-any.whl", hash = "sha256:bc6eaa49d1f9d870944f5280258db511e3a1ba5e2fbbed255488dceacae45d06"}, ] [package.dependencies] -flake8 = "*" -isort = ">=5.0.0,<6" +Django = ">=3.2" +phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phonenumbers\""} [package.extras] -test = ["pytest"] +phonenumbers = ["phonenumbers (>=7.0.2)"] +phonenumberslite = ["phonenumberslite (>=7.0.2)"] [[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." +name = "django-stubs" +version = "4.2.7" +description = "Mypy stubs for Django" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.8" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "django-stubs-4.2.7.tar.gz", hash = "sha256:8ccd2ff4ee5adf22b9e3b7b1a516d2e1c2191e9d94e672c35cc2bc3dd61e0f6b"}, + {file = "django_stubs-4.2.7-py3-none-any.whl", hash = "sha256:4cf4de258fa71adc6f2799e983091b9d46cfc67c6eebc68fe111218c9a62b3b8"}, ] +[package.dependencies] +django = "*" +django-stubs-ext = ">=4.2.7" +types-pytz = "*" +types-PyYAML = "*" +typing-extensions = "*" + [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +compatible-mypy = ["mypy (>=1.7.0,<1.8.0)"] [[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" +name = "django-stubs-ext" +version = "5.0.2" +description = "Monkey-patching and extensions for django-stubs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_stubs_ext-5.0.2-py3-none-any.whl", hash = "sha256:8d8efec5a86241266bec94a528fe21258ad90d78c67307f3ae5f36e81de97f12"}, + {file = "django_stubs_ext-5.0.2.tar.gz", hash = "sha256:409c62585d7f996cef5c760e6e27ea3ff29f961c943747e67519c837422cad32"}, +] + +[package.dependencies] +django = "*" +typing-extensions = "*" + +[[package]] +name = "djangorestframework" +version = "3.15.2" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"}, + {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "drf-yasg" +version = "1.21.7" +description = "Automated generation of real Swagger/OpenAPI 2.0 schemas from Django Rest Framework code." optional = false python-versions = ">=3.6" files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, + {file = "drf-yasg-1.21.7.tar.gz", hash = "sha256:4c3b93068b3dfca6969ab111155e4dd6f7b2d680b98778de8fd460b7837bdb0d"}, + {file = "drf_yasg-1.21.7-py3-none-any.whl", hash = "sha256:f85642072c35e684356475781b7ecf5d218fff2c6185c040664dd49f0a4be181"}, ] +[package.dependencies] +django = ">=2.2.16" +djangorestframework = ">=3.10.3" +inflection = ">=0.3.1" +packaging = ">=21.0" +pytz = ">=2021.1" +pyyaml = ">=5.1" +uritemplate = ">=3.0.0" + +[package.extras] +coreapi = ["coreapi (>=2.3.3)", "coreschema (>=0.0.4)"] +validation = ["swagger-spec-validator (>=2.1.0)"] + [[package]] -name = "mypy" -version = "1.5.1" -description = "Optional static typing for Python" +name = "email-validator" +version = "2.2.0" +description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, - {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, - {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, - {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, - {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, - {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, - {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, - {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, - {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, - {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, - {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, - {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, - {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, - {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, - {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, - {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, - {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, - {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, - {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, ] [package.dependencies] -mypy-extensions = ">=1.0.0" -typing-extensions = ">=4.1.0" +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "et-xmlfile" +version = "1.1.0" +description = "An implementation of lxml.xmlfile for the standard library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, + {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, +] + +[[package]] +name = "factory-boy" +version = "3.3.0" +description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." +optional = false +python-versions = ">=3.7" +files = [ + {file = "factory_boy-3.3.0-py2.py3-none-any.whl", hash = "sha256:a2cdbdb63228177aa4f1c52f4b6d83fab2b8623bf602c7dedd7eb83c0f69c04c"}, + {file = "factory_boy-3.3.0.tar.gz", hash = "sha256:bc76d97d1a65bbd9842a6d722882098eb549ec8ee1081f9fb2e8ff29f0c300f1"}, +] + +[package.dependencies] +Faker = ">=0.7.0" [package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -reports = ["lxml"] +dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "sqlalchemy-utils", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] [[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." +name = "faker" +version = "26.0.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-26.0.0-py3-none-any.whl", hash = "sha256:886ee28219be96949cd21ecc96c4c742ee1680e77f687b095202c8def1a08f06"}, + {file = "Faker-26.0.0.tar.gz", hash = "sha256:0f60978314973de02c00474c2ae899785a42b2cf4f41b7987e93c132a2b8a4a9"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "fastapi" +version = "0.112.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.112.0-py3-none-any.whl", hash = "sha256:3487ded9778006a45834b8c816ec4a48d522e2631ca9e75ec5a774f1b052f821"}, + {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"}, +] + +[package.dependencies] +email_validator = {version = ">=2.0.0", optional = true, markers = "extra == \"standard\""} +fastapi-cli = {version = ">=0.0.5", extras = ["standard"], optional = true, markers = "extra == \"standard\""} +httpx = {version = ">=0.23.0", optional = true, markers = "extra == \"standard\""} +jinja2 = {version = ">=2.11.2", optional = true, markers = "extra == \"standard\""} +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = {version = ">=0.0.7", optional = true, markers = "extra == \"standard\""} +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"], optional = true, markers = "extra == \"standard\""} + +[package.extras] +all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.5" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46"}, + {file = "fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f"}, +] + +[package.dependencies] +typer = ">=0.12.3" +uvicorn = {version = ">=0.15.0", extras = ["standard"]} + +[package.extras] +standard = ["uvicorn[standard] (>=0.15.0)"] + +[[package]] +name = "filelock" +version = "3.15.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.5" 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"}, + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] [[package]] -name = "pycodestyle" -version = "2.11.0" -description = "Python style guide checker" +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, - {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, ] +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + [[package]] -name = "pyflakes" -version = "3.1.0" -description = "passive checker of Python programs" +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + [[package]] -name = "sqlparse" -version = "0.4.4" -description = "A non-validating SQL parser." +name = "identify" +version = "2.5.36" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" optional = false python-versions = ">=3.5" files = [ - {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, - {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, + {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, + {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] +[package.dependencies] +MarkupSafe = ">=2.0" + [package.extras] -dev = ["build", "flake8"] -doc = ["sphinx"] -test = ["pytest", "pytest-cov"] +i18n = ["Babel (>=2.7)"] [[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" +name = "lxml" +version = "5.2.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:364d03207f3e603922d0d3932ef363d55bbf48e3647395765f9bfcbdf6d23632"}, + {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50127c186f191b8917ea2fb8b206fbebe87fd414a6084d15568c27d0a21d60db"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4f025ef3db1c6da4460dd27c118d8cd136d0391da4e387a15e48e5c975147"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981a06a3076997adf7c743dcd0d7a0415582661e2517c7d961493572e909aa1d"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aef5474d913d3b05e613906ba4090433c515e13ea49c837aca18bde190853dff"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e275ea572389e41e8b039ac076a46cb87ee6b8542df3fff26f5baab43713bca"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5b65529bb2f21ac7861a0e94fdbf5dc0daab41497d18223b46ee8515e5ad297"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bcc98f911f10278d1daf14b87d65325851a1d29153caaf146877ec37031d5f36"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:b47633251727c8fe279f34025844b3b3a3e40cd1b198356d003aa146258d13a2"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:fbc9d316552f9ef7bba39f4edfad4a734d3d6f93341232a9dddadec4f15d425f"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:13e69be35391ce72712184f69000cda04fc89689429179bc4c0ae5f0b7a8c21b"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b6a30a9ab040b3f545b697cb3adbf3696c05a3a68aad172e3fd7ca73ab3c835"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a233bb68625a85126ac9f1fc66d24337d6e8a0f9207b688eec2e7c880f012ec0"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:dfa7c241073d8f2b8e8dbc7803c434f57dbb83ae2a3d7892dd068d99e96efe2c"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a7aca7964ac4bb07680d5c9d63b9d7028cace3e2d43175cb50bba8c5ad33316"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae4073a60ab98529ab8a72ebf429f2a8cc612619a8c04e08bed27450d52103c0"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ffb2be176fed4457e445fe540617f0252a72a8bc56208fd65a690fdb1f57660b"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e290d79a4107d7d794634ce3e985b9ae4f920380a813717adf61804904dc4393"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96e85aa09274955bb6bd483eaf5b12abadade01010478154b0ec70284c1b1526"}, + {file = "lxml-5.2.2-cp310-cp310-win32.whl", hash = "sha256:f956196ef61369f1685d14dad80611488d8dc1ef00be57c0c5a03064005b0f30"}, + {file = "lxml-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:875a3f90d7eb5c5d77e529080d95140eacb3c6d13ad5b616ee8095447b1d22e7"}, + {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45f9494613160d0405682f9eee781c7e6d1bf45f819654eb249f8f46a2c22545"}, + {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0b3f2df149efb242cee2ffdeb6674b7f30d23c9a7af26595099afaf46ef4e88"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d28cb356f119a437cc58a13f8135ab8a4c8ece18159eb9194b0d269ec4e28083"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:657a972f46bbefdbba2d4f14413c0d079f9ae243bd68193cb5061b9732fa54c1"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b9ea10063efb77a965a8d5f4182806fbf59ed068b3c3fd6f30d2ac7bee734"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07542787f86112d46d07d4f3c4e7c760282011b354d012dc4141cc12a68cef5f"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:303f540ad2dddd35b92415b74b900c749ec2010e703ab3bfd6660979d01fd4ed"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2eb2227ce1ff998faf0cd7fe85bbf086aa41dfc5af3b1d80867ecfe75fb68df3"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:1d8a701774dfc42a2f0b8ccdfe7dbc140500d1049e0632a611985d943fcf12df"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:56793b7a1a091a7c286b5f4aa1fe4ae5d1446fe742d00cdf2ffb1077865db10d"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb00b549b13bd6d884c863554566095bf6fa9c3cecb2e7b399c4bc7904cb33b5"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a2569a1f15ae6c8c64108a2cd2b4a858fc1e13d25846be0666fc144715e32ab"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:8cf85a6e40ff1f37fe0f25719aadf443686b1ac7652593dc53c7ef9b8492b115"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d237ba6664b8e60fd90b8549a149a74fcc675272e0e95539a00522e4ca688b04"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b3f5016e00ae7630a4b83d0868fca1e3d494c78a75b1c7252606a3a1c5fc2ad"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23441e2b5339bc54dc949e9e675fa35efe858108404ef9aa92f0456929ef6fe8"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb0ba3e8566548d6c8e7dd82a8229ff47bd8fb8c2da237607ac8e5a1b8312e5"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:79d1fb9252e7e2cfe4de6e9a6610c7cbb99b9708e2c3e29057f487de5a9eaefa"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6dcc3d17eac1df7859ae01202e9bb11ffa8c98949dcbeb1069c8b9a75917e01b"}, + {file = "lxml-5.2.2-cp311-cp311-win32.whl", hash = "sha256:4c30a2f83677876465f44c018830f608fa3c6a8a466eb223535035fbc16f3438"}, + {file = "lxml-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:49095a38eb333aaf44c06052fd2ec3b8f23e19747ca7ec6f6c954ffea6dbf7be"}, + {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7429e7faa1a60cad26ae4227f4dd0459efde239e494c7312624ce228e04f6391"}, + {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50ccb5d355961c0f12f6cf24b7187dbabd5433f29e15147a67995474f27d1776"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc911208b18842a3a57266d8e51fc3cfaccee90a5351b92079beed912a7914c2"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33ce9e786753743159799fdf8e92a5da351158c4bfb6f2db0bf31e7892a1feb5"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec87c44f619380878bd49ca109669c9f221d9ae6883a5bcb3616785fa8f94c97"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08ea0f606808354eb8f2dfaac095963cb25d9d28e27edcc375d7b30ab01abbf6"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75a9632f1d4f698b2e6e2e1ada40e71f369b15d69baddb8968dcc8e683839b18"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74da9f97daec6928567b48c90ea2c82a106b2d500f397eeb8941e47d30b1ca85"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:0969e92af09c5687d769731e3f39ed62427cc72176cebb54b7a9d52cc4fa3b73"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:9164361769b6ca7769079f4d426a41df6164879f7f3568be9086e15baca61466"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d26a618ae1766279f2660aca0081b2220aca6bd1aa06b2cf73f07383faf48927"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab67ed772c584b7ef2379797bf14b82df9aa5f7438c5b9a09624dd834c1c1aaf"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3d1e35572a56941b32c239774d7e9ad724074d37f90c7a7d499ab98761bd80cf"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8268cbcd48c5375f46e000adb1390572c98879eb4f77910c6053d25cc3ac2c67"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e282aedd63c639c07c3857097fc0e236f984ceb4089a8b284da1c526491e3f3d"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfdc2bfe69e9adf0df4915949c22a25b39d175d599bf98e7ddf620a13678585"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4aefd911793b5d2d7a921233a54c90329bf3d4a6817dc465f12ffdfe4fc7b8fe"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8b8df03a9e995b6211dafa63b32f9d405881518ff1ddd775db4e7b98fb545e1c"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f11ae142f3a322d44513de1018b50f474f8f736bc3cd91d969f464b5bfef8836"}, + {file = "lxml-5.2.2-cp312-cp312-win32.whl", hash = "sha256:16a8326e51fcdffc886294c1e70b11ddccec836516a343f9ed0f82aac043c24a"}, + {file = "lxml-5.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:bbc4b80af581e18568ff07f6395c02114d05f4865c2812a1f02f2eaecf0bfd48"}, + {file = "lxml-5.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e3d9d13603410b72787579769469af730c38f2f25505573a5888a94b62b920f8"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38b67afb0a06b8575948641c1d6d68e41b83a3abeae2ca9eed2ac59892b36706"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c689d0d5381f56de7bd6966a4541bff6e08bf8d3871bbd89a0c6ab18aa699573"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:cf2a978c795b54c539f47964ec05e35c05bd045db5ca1e8366988c7f2fe6b3ce"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:739e36ef7412b2bd940f75b278749106e6d025e40027c0b94a17ef7968d55d56"}, + {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d8bbcd21769594dbba9c37d3c819e2d5847656ca99c747ddb31ac1701d0c0ed9"}, + {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:2304d3c93f2258ccf2cf7a6ba8c761d76ef84948d87bf9664e14d203da2cd264"}, + {file = "lxml-5.2.2-cp36-cp36m-win32.whl", hash = "sha256:02437fb7308386867c8b7b0e5bc4cd4b04548b1c5d089ffb8e7b31009b961dc3"}, + {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, + {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, + {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, + {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, + {file = "lxml-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ed07b3062b055d7a7f9d6557a251cc655eed0b3152b76de619516621c56f5d3"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60fdd125d85bf9c279ffb8e94c78c51b3b6a37711464e1f5f31078b45002421"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7e24cb69ee5f32e003f50e016d5fde438010c1022c96738b04fc2423e61706"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cfafd56887eaed93d07bc4547abd5e09d837a002b791e9767765492a75883f"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19b4e485cd07b7d83e3fe3b72132e7df70bfac22b14fe4bf7a23822c3a35bff5"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7ce7ad8abebe737ad6143d9d3bf94b88b93365ea30a5b81f6877ec9c0dee0a48"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e49b052b768bb74f58c7dda4e0bdf7b79d43a9204ca584ffe1fb48a6f3c84c66"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d14a0d029a4e176795cef99c056d58067c06195e0c7e2dbb293bf95c08f772a3"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:be49ad33819d7dcc28a309b86d4ed98e1a65f3075c6acd3cd4fe32103235222b"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a6d17e0370d2516d5bb9062c7b4cb731cff921fc875644c3d751ad857ba9c5b1"}, + {file = "lxml-5.2.2-cp38-cp38-win32.whl", hash = "sha256:5b8c041b6265e08eac8a724b74b655404070b636a8dd6d7a13c3adc07882ef30"}, + {file = "lxml-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:f61efaf4bed1cc0860e567d2ecb2363974d414f7f1f124b1df368bbf183453a6"}, + {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb91819461b1b56d06fa4bcf86617fac795f6a99d12239fb0c68dbeba41a0a30"}, + {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d4ed0c7cbecde7194cd3228c044e86bf73e30a23505af852857c09c24e77ec5d"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54401c77a63cc7d6dc4b4e173bb484f28a5607f3df71484709fe037c92d4f0ed"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:625e3ef310e7fa3a761d48ca7ea1f9d8718a32b1542e727d584d82f4453d5eeb"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:519895c99c815a1a24a926d5b60627ce5ea48e9f639a5cd328bda0515ea0f10c"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7079d5eb1c1315a858bbf180000757db8ad904a89476653232db835c3114001"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:343ab62e9ca78094f2306aefed67dcfad61c4683f87eee48ff2fd74902447726"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:cd9e78285da6c9ba2d5c769628f43ef66d96ac3085e59b10ad4f3707980710d3"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:546cf886f6242dff9ec206331209db9c8e1643ae642dea5fdbecae2453cb50fd"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:02f6a8eb6512fdc2fd4ca10a49c341c4e109aa6e9448cc4859af5b949622715a"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:339ee4a4704bc724757cd5dd9dc8cf4d00980f5d3e6e06d5847c1b594ace68ab"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0a028b61a2e357ace98b1615fc03f76eb517cc028993964fe08ad514b1e8892d"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f90e552ecbad426eab352e7b2933091f2be77115bb16f09f78404861c8322981"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d83e2d94b69bf31ead2fa45f0acdef0757fa0458a129734f59f67f3d2eb7ef32"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a02d3c48f9bb1e10c7788d92c0c7db6f2002d024ab6e74d6f45ae33e3d0288a3"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d68ce8e7b2075390e8ac1e1d3a99e8b6372c694bbe612632606d1d546794207"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:453d037e09a5176d92ec0fd282e934ed26d806331a8b70ab431a81e2fbabf56d"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3b019d4ee84b683342af793b56bb35034bd749e4cbdd3d33f7d1107790f8c472"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb3942960f0beb9f46e2a71a3aca220d1ca32feb5a398656be934320804c0df9"}, + {file = "lxml-5.2.2-cp39-cp39-win32.whl", hash = "sha256:ac6540c9fff6e3813d29d0403ee7a81897f1d8ecc09a8ff84d2eea70ede1cdbf"}, + {file = "lxml-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:610b5c77428a50269f38a534057444c249976433f40f53e3b47e68349cca1425"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b537bd04d7ccd7c6350cdaaaad911f6312cbd61e6e6045542f781c7f8b2e99d2"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4820c02195d6dfb7b8508ff276752f6b2ff8b64ae5d13ebe02e7667e035000b9"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a09f6184f17a80897172863a655467da2b11151ec98ba8d7af89f17bf63dae"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76acba4c66c47d27c8365e7c10b3d8016a7da83d3191d053a58382311a8bf4e1"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b128092c927eaf485928cec0c28f6b8bead277e28acf56800e972aa2c2abd7a2"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ae791f6bd43305aade8c0e22f816b34f3b72b6c820477aab4d18473a37e8090b"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a2f6a1bc2460e643785a2cde17293bd7a8f990884b822f7bca47bee0a82fc66b"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8d351ff44c1638cb6e980623d517abd9f580d2e53bfcd18d8941c052a5a009"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec4bd9133420c5c52d562469c754f27c5c9e36ee06abc169612c959bd7dbb07"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:55ce6b6d803890bd3cc89975fca9de1dff39729b43b73cb15ddd933b8bc20484"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ab6a358d1286498d80fe67bd3d69fcbc7d1359b45b41e74c4a26964ca99c3f8"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:06668e39e1f3c065349c51ac27ae430719d7806c026fec462e5693b08b95696b"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9cd5323344d8ebb9fb5e96da5de5ad4ebab993bbf51674259dbe9d7a18049525"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89feb82ca055af0fe797a2323ec9043b26bc371365847dbe83c7fd2e2f181c34"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e481bba1e11ba585fb06db666bfc23dbe181dbafc7b25776156120bf12e0d5a6"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d6c6ea6a11ca0ff9cd0390b885984ed31157c168565702959c25e2191674a14"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3d98de734abee23e61f6b8c2e08a88453ada7d6486dc7cdc82922a03968928db"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:69ab77a1373f1e7563e0fb5a29a8440367dec051da6c7405333699d07444f511"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34e17913c431f5ae01d8658dbf792fdc457073dcdfbb31dc0cc6ab256e664a8d"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f8757b03208c3f50097761be2dea0aba02e94f0dc7023ed73a7bb14ff11eb0"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a520b4f9974b0a0a6ed73c2154de57cdfd0c8800f4f15ab2b73238ffed0b36e"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5e097646944b66207023bc3c634827de858aebc226d5d4d6d16f0b77566ea182"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b5e4ef22ff25bfd4ede5f8fb30f7b24446345f3e79d9b7455aef2836437bc38a"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff69a9a0b4b17d78170c73abe2ab12084bdf1691550c5629ad1fe7849433f324"}, + {file = "lxml-5.2.2.tar.gz", hash = "sha256:bb2dc4898180bea79863d5487e5f9c7c34297414bad54bcd0f0852aee9cfdb87"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.10)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] -name = "tzdata" -version = "2023.3" -description = "Provider of IANA time zone data" +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=2" +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "mypy" +version = "1.10.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +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"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "numpy" +version = "2.0.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:04494f6ec467ccb5369d1808570ae55f6ed9b5809d7f035059000a37b8d7e86f"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2635dbd200c2d6faf2ef9a0d04f0ecc6b13b3cad54f7c67c61155138835515d2"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:0a43f0974d501842866cc83471bdb0116ba0dffdbaac33ec05e6afed5b615238"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:8d83bb187fb647643bd56e1ae43f273c7f4dbcdf94550d7938cfc32566756514"}, + {file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79e843d186c8fb1b102bef3e2bc35ef81160ffef3194646a7fdd6a73c6b97196"}, + {file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7696c615765091cc5093f76fd1fa069870304beaccfd58b5dcc69e55ef49c1"}, + {file = "numpy-2.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b4c76e3d4c56f145d41b7b6751255feefae92edbc9a61e1758a98204200f30fc"}, + {file = "numpy-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd3a644e4807e73b4e1867b769fbf1ce8c5d80e7caaef0d90dcdc640dfc9787"}, + {file = "numpy-2.0.0-cp310-cp310-win32.whl", hash = "sha256:cee6cc0584f71adefe2c908856ccc98702baf95ff80092e4ca46061538a2ba98"}, + {file = "numpy-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:ed08d2703b5972ec736451b818c2eb9da80d66c3e84aed1deeb0c345fefe461b"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad0c86f3455fbd0de6c31a3056eb822fc939f81b1618f10ff3406971893b62a5"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7f387600d424f91576af20518334df3d97bc76a300a755f9a8d6e4f5cadd289"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:34f003cb88b1ba38cb9a9a4a3161c1604973d7f9d5552c38bc2f04f829536609"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b6f6a8f45d0313db07d6d1d37bd0b112f887e1369758a5419c0370ba915b3871"}, + {file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f64641b42b2429f56ee08b4f427a4d2daf916ec59686061de751a55aafa22e4"}, + {file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7039a136017eaa92c1848152827e1424701532ca8e8967fe480fe1569dae581"}, + {file = "numpy-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46e161722e0f619749d1cd892167039015b2c2817296104487cd03ed4a955995"}, + {file = "numpy-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0e50842b2295ba8414c8c1d9d957083d5dfe9e16828b37de883f51fc53c4016f"}, + {file = "numpy-2.0.0-cp311-cp311-win32.whl", hash = "sha256:2ce46fd0b8a0c947ae047d222f7136fc4d55538741373107574271bc00e20e8f"}, + {file = "numpy-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd6acc766814ea6443628f4e6751d0da6593dae29c08c0b2606164db026970c"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:354f373279768fa5a584bac997de6a6c9bc535c482592d7a813bb0c09be6c76f"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d2f62e55a4cd9c58c1d9a1c9edaedcd857a73cb6fda875bf79093f9d9086f85"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1e72728e7501a450288fc8e1f9ebc73d90cfd4671ebbd631f3e7857c39bd16f2"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:84554fc53daa8f6abf8e8a66e076aff6ece62de68523d9f665f32d2fc50fd66e"}, + {file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73aafd1afca80afecb22718f8700b40ac7cab927b8abab3c3e337d70e10e5a2"}, + {file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49d9f7d256fbc804391a7f72d4a617302b1afac1112fac19b6c6cec63fe7fe8a"}, + {file = "numpy-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0ec84b9ba0654f3b962802edc91424331f423dcf5d5f926676e0150789cb3d95"}, + {file = "numpy-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:feff59f27338135776f6d4e2ec7aeeac5d5f7a08a83e80869121ef8164b74af9"}, + {file = "numpy-2.0.0-cp312-cp312-win32.whl", hash = "sha256:c5a59996dc61835133b56a32ebe4ef3740ea5bc19b3983ac60cc32be5a665d54"}, + {file = "numpy-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a356364941fb0593bb899a1076b92dfa2029f6f5b8ba88a14fd0984aaf76d0df"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e61155fae27570692ad1d327e81c6cf27d535a5d7ef97648a17d922224b216de"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4554eb96f0fd263041baf16cf0881b3f5dafae7a59b1049acb9540c4d57bc8cb"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:903703372d46bce88b6920a0cd86c3ad82dae2dbef157b5fc01b70ea1cfc430f"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:3e8e01233d57639b2e30966c63d36fcea099d17c53bf424d77f088b0f4babd86"}, + {file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cde1753efe513705a0c6d28f5884e22bdc30438bf0085c5c486cdaff40cd67a"}, + {file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821eedb7165ead9eebdb569986968b541f9908979c2da8a4967ecac4439bae3d"}, + {file = "numpy-2.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a1712c015831da583b21c5bfe15e8684137097969c6d22e8316ba66b5baabe4"}, + {file = "numpy-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9c27f0946a3536403efb0e1c28def1ae6730a72cd0d5878db38824855e3afc44"}, + {file = "numpy-2.0.0-cp39-cp39-win32.whl", hash = "sha256:63b92c512d9dbcc37f9d81b123dec99fdb318ba38c8059afc78086fe73820275"}, + {file = "numpy-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:3f6bed7f840d44c08ebdb73b1825282b801799e325bcbdfa6bc5c370e5aecc65"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9416a5c2e92ace094e9f0082c5fd473502c91651fb896bc17690d6fc475128d6"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:17067d097ed036636fa79f6a869ac26df7db1ba22039d962422506640314933a"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ecb5b0582cd125f67a629072fed6f83562d9dd04d7e03256c9829bdec027ad"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cef04d068f5fb0518a77857953193b6bb94809a806bd0a14983a8f12ada060c9"}, + {file = "numpy-2.0.0.tar.gz", hash = "sha256:cf5d1c9e6837f8af9f92b6bd3e86d513cdc11f60fd62185cc49ec7d1aba34864"}, +] + +[[package]] +name = "opencv-python" +version = "4.10.0.84" +description = "Wrapper package for OpenCV python bindings." +optional = false +python-versions = ">=3.6" +files = [ + {file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236"}, + {file = "opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, + {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, + {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, +] + +[package.dependencies] +et-xmlfile = "*" + +[[package]] +name = "openpyxl-stubs" +version = "0.1.25" +description = "Type stubs for openpyxl" +optional = false +python-versions = "*" +files = [ + {file = "openpyxl-stubs-0.1.25.tar.gz", hash = "sha256:108b112df072f7645ca356eacdd5730b1bd986c67ae33366a4a13c6879c369e7"}, + {file = "openpyxl_stubs-0.1.25-py3-none-any.whl", hash = "sha256:db29f7804993b4a46b155fc4be45314c14538cb475b00591d8096e5af486abf1"}, +] + +[package.dependencies] +mypy = ">=0.720" +openpyxl = ">=3.0.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "phonenumbers" +version = "8.13.39" +description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." +optional = false +python-versions = "*" +files = [ + {file = "phonenumbers-8.13.39-py2.py3-none-any.whl", hash = "sha256:3ad2d086fa71e7eef409001b9195ac54bebb0c6e3e752209b558ca192c9229a0"}, + {file = "phonenumbers-8.13.39.tar.gz", hash = "sha256:db7ca4970d206b2056231105300753b1a5b229f43416f8c2b3010e63fbb68d77"}, +] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "pydantic" +version = "2.8.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.20.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-django" +version = "4.8.0" +description = "A Django plugin for pytest." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"}, + {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +testing = ["Django", "django-configurations (>=2.0)"] + +[[package]] +name = "pytest-subtests" +version = "0.12.1" +description = "unittest subTest() support and subtests fixture" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-subtests-0.12.1.tar.gz", hash = "sha256:d6605dcb88647e0b7c1889d027f8ef1c17d7a2c60927ebfdc09c7b0d8120476d"}, + {file = "pytest_subtests-0.12.1-py3-none-any.whl", hash = "sha256:100d9f7eb966fc98efba7026c802812ae327e8b5b37181fb260a2ea93226495c"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +pytest = ">=7.0" + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-docx" +version = "1.1.2" +description = "Create, read, and update Microsoft Word .docx files." +optional = false +python-versions = ">=3.7" +files = [ + {file = "python_docx-1.1.2-py3-none-any.whl", hash = "sha256:08c20d6058916fb19853fcf080f7f42b6270d89eac9fa5f8c15f691c0017fabe"}, + {file = "python_docx-1.1.2.tar.gz", hash = "sha256:0cf1f22e95b9002addca7948e16f2cd7acdfd498047f1941ca5d293db7762efd"}, +] + +[package.dependencies] +lxml = ">=3.1.0" +typing-extensions = ">=4.9.0" + +[[package]] +name = "python-dotenv" +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" +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"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruff" +version = "0.4.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, + {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, + {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, + {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, + {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, + {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, +] + +[[package]] +name = "setuptools" +version = "70.1.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.1.1-py3-none-any.whl", hash = "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95"}, + {file = "setuptools-70.1.1.tar.gz", hash = "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlparse" +version = "0.5.0" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sqlparse-0.5.0-py3-none-any.whl", hash = "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"}, + {file = "sqlparse-0.5.0.tar.gz", hash = "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"}, +] + +[package.extras] +dev = ["build", "hatch"] +doc = ["sphinx"] + +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "types-openpyxl" +version = "3.1.4.20240626" +description = "Typing stubs for openpyxl" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-openpyxl-3.1.4.20240626.tar.gz", hash = "sha256:e3d677994fed637c54ec2115b2c860ec286977e76302c12a32fd5e4725698ef8"}, + {file = "types_openpyxl-3.1.4.20240626-py3-none-any.whl", hash = "sha256:42a9a937ab422f8b1b5968d8baedb6557f10d418d1e2884ed8104295ea3f374b"}, +] + +[[package]] +name = "types-pillow" +version = "10.2.0.20240520" +description = "Typing stubs for Pillow" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-Pillow-10.2.0.20240520.tar.gz", hash = "sha256:130b979195465fa1e1676d8e81c9c7c30319e8e95b12fae945e8f0d525213107"}, + {file = "types_Pillow-10.2.0.20240520-py3-none-any.whl", hash = "sha256:33c36494b380e2a269bb742181bea5d9b00820367822dbd3760f07210a1da23d"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + +[[package]] +name = "types-pytz" +version = "2024.1.0.20240417" +description = "Typing stubs for pytz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"}, + {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20240311" +description = "Typing stubs for PyYAML" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"}, + {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +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"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +optional = false +python-versions = ">=3.6" +files = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.30.5" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"}, + {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "virtualenv" +version = "20.26.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "watchfiles" +version = "0.23.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.23.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4"}, + {file = "watchfiles-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1"}, + {file = "watchfiles-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207"}, + {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b"}, + {file = "watchfiles-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981"}, + {file = "watchfiles-0.23.0-cp310-none-win32.whl", hash = "sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42"}, + {file = "watchfiles-0.23.0-cp310-none-win_amd64.whl", hash = "sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75"}, + {file = "watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab"}, + {file = "watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788"}, + {file = "watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1"}, + {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220"}, + {file = "watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320"}, + {file = "watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b"}, + {file = "watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e"}, + {file = "watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304"}, + {file = "watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5"}, + {file = "watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f"}, + {file = "watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0"}, + {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c"}, + {file = "watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581"}, + {file = "watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75"}, + {file = "watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb"}, + {file = "watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8"}, + {file = "watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9"}, + {file = "watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d"}, + {file = "watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0"}, + {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef"}, + {file = "watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1"}, + {file = "watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b"}, + {file = "watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff"}, + {file = "watchfiles-0.23.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366"}, + {file = "watchfiles-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03"}, + {file = "watchfiles-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48"}, + {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810"}, + {file = "watchfiles-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3"}, + {file = "watchfiles-0.23.0-cp38-none-win32.whl", hash = "sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b"}, + {file = "watchfiles-0.23.0-cp38-none-win_amd64.whl", hash = "sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472"}, + {file = "watchfiles-0.23.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3"}, + {file = "watchfiles-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd"}, + {file = "watchfiles-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765"}, + {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647"}, + {file = "watchfiles-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6"}, + {file = "watchfiles-0.23.0-cp39-none-win32.whl", hash = "sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e"}, + {file = "watchfiles-0.23.0-cp39-none-win_amd64.whl", hash = "sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed"}, + {file = "watchfiles-0.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c"}, + {file = "watchfiles-0.23.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47"}, + {file = "watchfiles-0.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66"}, + {file = "watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[package]] +name = "yadisk" +version = "3.1.0" +description = "Библиотека-клиент REST API Яндекс.Диска / Yandex.Disk REST API client library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "yadisk-3.1.0-py3-none-any.whl", hash = "sha256:cddc3a07f541bb005f135e8fbeff6980797494e54b6703c26830ca64d38e00e1"}, + {file = "yadisk-3.1.0.tar.gz", hash = "sha256:7228fad42007e1ff8becc48ca9bfa357398f68fa50fe597672d4bf92d99e6685"}, +] + +[package.extras] +aiohttp = ["aiohttp"] +async-defaults = ["aiofiles", "httpx"] +async-files = ["aiofiles"] +httpx = ["httpx"] +pycurl = ["pycurl"] +requests = ["requests"] +sync-defaults = ["requests"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "7ac36ead9691c4283ba34ee7f2f86d25836442ac7f91bd767b482084b56cb4b7" +content-hash = "b99147fc299b35960293efae5d15509d6f1a341dd16aa51a60bd3a2552d8f356" diff --git a/pyproject.toml b/pyproject.toml index 695a64a3..ac25ba25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,25 +4,145 @@ version = "0.1.0" description = "" authors = [] readme = "README.md" -packages = [{ include = "adaptive_hockey_federation" }] [tool.poetry.dependencies] python = "^3.11" django = "^4.2.6" +gunicorn = "^21.2.0" +openpyxl = "^3.1.2" +types-openpyxl = "^3.1.0.24" +openpyxl-stubs = "^0.1.25" +click = "^8.1.7" +wrapt = "^1.16.0" +django-phonenumber-field = {extras = ["phonenumbers"], version = "^7.2.0"} +pytest-django = "^4.7.0" +psycopg2-binary = "2.9.9" +django-environ = "^0.11.2" +django-extensions = "^3.2.3" +pillow = "^10.2.0" +types-python-dateutil = "^2.8.19.20240106" +pytest-subtests = "^0.12.1" +djangorestframework = "^3.15.1" +drf-yasg = "^1.21.7" + +yadisk = "^3.1.0" [tool.poetry.group.dev.dependencies] -isort = "^5.12.0" +setuptools = "^70.0.0" +ruff = "^0.4.5" +pre-commit = "3.5.0" +django-debug-toolbar = "^4.2.0" +django-extensions = "^3.2.3" +factory-boy = "^3.3.0" +types-Pillow = "*" +opencv-python = "^4.10.0.84" +fastapi = {extras = ["standard"], version = "^0.112.0"} [tool.poetry.group.test.dependencies] -flake8 = "^6.1.0" -flake8-isort = "^6.1.0" -mypy = "^1.5.1" - +django-stubs = "^4.2.4" -[tool.isort] -multi_line_output = 3 -include_trailing_comma = true +[tool.poetry.group.parser.dependencies] +python-docx = "^1.0.1" +click = "^8.1.7" +requests = "^2.31.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.ruff] +target-version = "py311" +exclude = [ + "config", + "*migrations/*", + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] +line-length = 79 +indent-width = 4 +output-format = "grouped" + +[tool.ruff.lint] +select = [ + # pep8-naming + "N", + #pyflakes + "F", + #pycodestyle + "E", + "W", + #mccabe + "C90", + #isort + "I", + #pydocstyle + "D", + #flake8-bugbear + "B", + #flake8-commas + "COM", + #flake8-django + "DJ", + #flake8-print + "T20", + #flake8-pie + "PIE", + #flake8-quotes + "Q", + #eradicate + "ERA", + #flake8-type-checking + "TCH", +] + +ignore = ["D100", "D103", "T201", "D104", "D106", "D203", "B012", "B904", "COM819", "D212", "I001", "ERA001", "N999"] + +fixable = ["ALL"] +unfixable = [] + +[tool.ruff.format] +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false +quote-style = "double" + +[tool.ruff.lint."flake8-quotes"] +inline-quotes = "double" + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] +"settings*" = ["E501", "F405"] + +[tool.django-stubs] +django_settings_module = "adaptive_hockey_federation.core.config.dev_settings" + +[tool.pytest.ini_options] +DJANGO_SETTINGS_MODULE = "adaptive_hockey_federation.core.config.test_settings" +python_files = ["*_test.py"] +console_output_style = "progress" +verbosity_test_cases = 2 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..f1e27fcb --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +DJANGO_SETTINGS_MODULE = adaptive_hockey_federation.core.config.test_settings +testpaths = tests/ +python_files = *_test.py +console_output_style = progress +addopts = -vv diff --git a/requirements/develop.txt b/requirements/develop.txt new file mode 100644 index 00000000..f83ebd67 --- /dev/null +++ b/requirements/develop.txt @@ -0,0 +1,84 @@ +annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0" +anyio==4.4.0 ; python_version >= "3.11" and python_version < "4.0" +asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" +attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" +certifi==2024.6.2 ; python_version >= "3.11" and python_version < "4.0" +cfgv==3.4.0 ; python_version >= "3.11" and python_version < "4.0" +click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" +colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") +distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" +django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" +django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" +django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" +django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" +django==4.2.13 ; python_version >= "3.11" and python_version < "4.0" +djangorestframework==3.15.2 ; python_version >= "3.11" and python_version < "4.0" +dnspython==2.6.1 ; python_version >= "3.11" and python_version < "4.0" +drf-yasg==1.21.7 ; python_version >= "3.11" and python_version < "4.0" +email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0" +et-xmlfile==1.1.0 ; python_version >= "3.11" and python_version < "4.0" +factory-boy==3.3.0 ; python_version >= "3.11" and python_version < "4.0" +faker==26.0.0 ; python_version >= "3.11" and python_version < "4.0" +fastapi-cli[standard]==0.0.5 ; python_version >= "3.11" and python_version < "4.0" +fastapi[standard]==0.112.0 ; python_version >= "3.11" and python_version < "4.0" +filelock==3.15.4 ; python_version >= "3.11" and python_version < "4.0" +gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0" +h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" +httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0" +httptools==0.6.1 ; python_version >= "3.11" and python_version < "4.0" +httpx==0.27.0 ; python_version >= "3.11" and python_version < "4.0" +identify==2.5.36 ; python_version >= "3.11" and python_version < "4.0" +idna==3.7 ; python_version >= "3.11" and python_version < "4.0" +inflection==0.5.1 ; python_version >= "3.11" and python_version < "4.0" +iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +jinja2==3.1.4 ; python_version >= "3.11" and python_version < "4.0" +markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0" +markupsafe==2.1.5 ; python_version >= "3.11" and python_version < "4.0" +mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0" +mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0" +mypy==1.10.1 ; python_version >= "3.11" and python_version < "4.0" +nodeenv==1.9.1 ; python_version >= "3.11" and python_version < "4.0" +numpy==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +opencv-python==4.10.0.84 ; python_version >= "3.11" and python_version < "4.0" +openpyxl-stubs==0.1.25 ; python_version >= "3.11" and python_version < "4.0" +openpyxl==3.1.5 ; python_version >= "3.11" and python_version < "4.0" +packaging==24.1 ; python_version >= "3.11" and python_version < "4.0" +phonenumbers==8.13.39 ; python_version >= "3.11" and python_version < "4.0" +pillow==10.3.0 ; python_version >= "3.11" and python_version < "4.0" +platformdirs==4.2.2 ; python_version >= "3.11" and python_version < "4.0" +pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0" +pre-commit==3.5.0 ; python_version >= "3.11" and python_version < "4.0" +psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0" +pydantic-core==2.20.1 ; python_version >= "3.11" and python_version < "4.0" +pydantic==2.8.2 ; python_version >= "3.11" and python_version < "4.0" +pygments==2.18.0 ; python_version >= "3.11" and python_version < "4.0" +pytest-django==4.8.0 ; python_version >= "3.11" and python_version < "4.0" +pytest-subtests==0.12.1 ; python_version >= "3.11" and python_version < "4.0" +pytest==8.2.2 ; python_version >= "3.11" and python_version < "4.0" +python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0" +python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" +python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0" +pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" +pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" +rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0" +ruff==0.4.10 ; python_version >= "3.11" and python_version < "4.0" +setuptools==70.1.1 ; python_version >= "3.11" and python_version < "4.0" +shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0" +six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" +sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" +sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" +starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0" +typer==0.12.3 ; python_version >= "3.11" and python_version < "4.0" +types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" +types-pillow==10.2.0.20240520 ; python_version >= "3.11" and python_version < "4.0" +types-python-dateutil==2.9.0.20240316 ; python_version >= "3.11" and python_version < "4.0" +typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0" +tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "win32" +uritemplate==4.1.1 ; python_version >= "3.11" and python_version < "4.0" +uvicorn[standard]==0.30.5 ; python_version >= "3.11" and python_version < "4.0" +uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0" +virtualenv==20.26.3 ; python_version >= "3.11" and python_version < "4.0" +watchfiles==0.23.0 ; python_version >= "3.11" and python_version < "4.0" +websockets==12.0 ; python_version >= "3.11" and python_version < "4.0" +wrapt==1.16.0 ; python_version >= "3.11" and python_version < "4.0" +yadisk==3.1.0 ; python_version >= "3.11" and python_version < "4.0" diff --git a/requirements/production.txt b/requirements/production.txt new file mode 100644 index 00000000..2ffd0322 --- /dev/null +++ b/requirements/production.txt @@ -0,0 +1,100 @@ + +amqp==5.2.0 ; python_version >= "3.11" and python_version < "4.0" +annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0" +anyio==4.4.0 ; python_version >= "3.11" and python_version < "4.0" +asgiref==3.8.1 ; python_version >= "3.11" and python_version < "4.0" +attrs==23.2.0 ; python_version >= "3.11" and python_version < "4.0" +billiard==4.2.0 ; python_version >= "3.11" and python_version < "4.0" +certifi==2024.6.2 ; python_version >= "3.11" and python_version < "4.0" +cfgv==3.4.0 ; python_version >= "3.11" and python_version < "4.0" +click-didyoumean==0.3.1 ; python_version >= "3.11" and python_version < "4.0" +click-plugins==1.1.1 ; python_version >= "3.11" and python_version < "4.0" +click-repl==0.3.0 ; python_version >= "3.11" and python_version < "4.0" +click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" +colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32") +distlib==0.3.8 ; python_version >= "3.11" and python_version < "4.0" +django-debug-toolbar==4.4.2 ; python_version >= "3.11" and python_version < "4.0" +django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4" +django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" +django-phonenumber-field[phonenumbers]==7.3.0 ; python_version >= "3.11" and python_version < "4.0" +django==4.2.13 ; python_version >= "3.11" and python_version < "4.0" +djangorestframework==3.15.2 ; python_version >= "3.11" and python_version < "4.0" +dnspython==2.6.1 ; python_version >= "3.11" and python_version < "4.0" +drf-yasg==1.21.7 ; python_version >= "3.11" and python_version < "4.0" +email-validator==2.2.0 ; python_version >= "3.11" and python_version < "4.0" +et-xmlfile==1.1.0 ; python_version >= "3.11" and python_version < "4.0" +factory-boy==3.3.0 ; python_version >= "3.11" and python_version < "4.0" +faker==26.0.0 ; python_version >= "3.11" and python_version < "4.0" +fastapi-cli[standard]==0.0.5 ; python_version >= "3.11" and python_version < "4.0" +fastapi[standard]==0.112.0 ; python_version >= "3.11" and python_version < "4.0" +filelock==3.15.4 ; python_version >= "3.11" and python_version < "4.0" +gunicorn==21.2.0 ; python_version >= "3.11" and python_version < "4.0" +h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" +httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0" +httptools==0.6.1 ; python_version >= "3.11" and python_version < "4.0" +httpx==0.27.0 ; python_version >= "3.11" and python_version < "4.0" +humanize==4.9.0 ; python_version >= "3.11" and python_version < "4.0" +identify==2.5.36 ; python_version >= "3.11" and python_version < "4.0" +idna==3.7 ; python_version >= "3.11" and python_version < "4.0" +inflection==0.5.1 ; python_version >= "3.11" and python_version < "4.0" +iniconfig==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +jinja2==3.1.4 ; python_version >= "3.11" and python_version < "4.0" +kombu==5.3.7 ; python_version >= "3.11" and python_version < "4.0" +markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0" +markupsafe==2.1.5 ; python_version >= "3.11" and python_version < "4.0" +mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0" +mypy-extensions==1.0.0 ; python_version >= "3.11" and python_version < "4.0" +mypy==1.10.1 ; python_version >= "3.11" and python_version < "4.0" +nodeenv==1.9.1 ; python_version >= "3.11" and python_version < "4.0" +numpy==2.0.0 ; python_version >= "3.11" and python_version < "4.0" +opencv-python-headless==4.10.0.84 ; python_version >= "3.11" and python_version < "4.0" +openpyxl-stubs==0.1.25 ; python_version >= "3.11" and python_version < "4.0" +openpyxl==3.1.5 ; python_version >= "3.11" and python_version < "4.0" +packaging==24.1 ; python_version >= "3.11" and python_version < "4.0" +phonenumbers==8.13.39 ; python_version >= "3.11" and python_version < "4.0" +pillow==10.3.0 ; python_version >= "3.11" and python_version < "4.0" +platformdirs==4.2.2 ; python_version >= "3.11" and python_version < "4.0" +pluggy==1.5.0 ; python_version >= "3.11" and python_version < "4.0" +pre-commit==3.5.0 ; python_version >= "3.11" and python_version < "4.0" +prometheus-client==0.20.0 ; python_version >= "3.11" and python_version < "4.0" +prompt-toolkit==3.0.47 ; python_version >= "3.11" and python_version < "4.0" +psycopg2-binary==2.9.9 ; python_version >= "3.11" and python_version < "4.0" +pydantic-core==2.20.1 ; python_version >= "3.11" and python_version < "4.0" +pydantic==2.8.2 ; python_version >= "3.11" and python_version < "4.0" +pygments==2.18.0 ; python_version >= "3.11" and python_version < "4.0" +pytest-django==4.8.0 ; python_version >= "3.11" and python_version < "4.0" +pytest-subtests==0.12.1 ; python_version >= "3.11" and python_version < "4.0" +pytest==8.2.2 ; python_version >= "3.11" and python_version < "4.0" +python-dateutil==2.9.0.post0 ; python_version >= "3.11" and python_version < "4.0" +python-dotenv==1.0.1 ; python_version >= "3.11" and python_version < "4.0" +python-multipart==0.0.9 ; python_version >= "3.11" and python_version < "4.0" +pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" +pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" +rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0" +requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" +ruff==0.4.10 ; python_version >= "3.11" and python_version < "4.0" +setuptools==70.1.1 ; python_version >= "3.11" and python_version < "4.0" +shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0" +six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" +sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" +pytz==2024.1 ; python_version >= "3.11" and python_version < "4.0" +pyyaml==6.0.1 ; python_version >= "3.11" and python_version < "4.0" +sqlparse==0.5.0 ; python_version >= "3.11" and python_version < "4.0" +starlette==0.37.2 ; python_version >= "3.11" and python_version < "4.0" +tornado==6.4.1 ; python_version >= "3.11" and python_version < "4.0" +typer==0.12.3 ; python_version >= "3.11" and python_version < "4.0" +types-openpyxl==3.1.4.20240626 ; python_version >= "3.11" and python_version < "4.0" +types-pillow==10.2.0.20240520 ; python_version >= "3.11" and python_version < "4.0" +types-python-dateutil==2.9.0.20240316 ; python_version >= "3.11" and python_version < "4.0" +typing-extensions==4.12.2 ; python_version >= "3.11" and python_version < "4.0" +tzdata==2024.1 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "win32" +uritemplate==4.1.1 ; python_version >= "3.11" and python_version < "4.0" +uvicorn[standard]==0.30.5 ; python_version >= "3.11" and python_version < "4.0" +uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.11" and python_version < "4.0" +vine==5.1.0 ; python_version >= "3.11" and python_version < "4.0" +virtualenv==20.26.3 ; python_version >= "3.11" and python_version < "4.0" +watchfiles==0.23.0 ; python_version >= "3.11" and python_version < "4.0" +wcwidth==0.2.13 ; python_version >= "3.11" and python_version < "4.0" +websockets==12.0 ; python_version >= "3.11" and python_version < "4.0" +wrapt==1.16.0 ; python_version >= "3.11" and python_version < "4.0" +yadisk==3.1.0 ; python_version >= "3.11" and python_version < "4.0" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..d725b0b5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,17 @@ +[flake8] +ignore = + W503, + F811 +exclude = + tests/, + */migrations/, + */config/, + venv/, + .venv/, + env/ +per-file-ignores = + */settings.py:E501 +max-complexity = 10 + +[mypy.plugins.django-stubs] +django_settings_module = adaptive_hockey_federation.core.config.dev_settings From 85c1c19c54cbd8318f1087f85506107a12fbc3c9 Mon Sep 17 00:00:00 2001 From: OlegGsk <142893688+OlegGsk@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:31:49 +0500 Subject: [PATCH 126/126] Feature/modify import commands (#564) * update import import-db.py, export-db.py, docker-compose.prod.yaml * fix deploy * on review --- .github/workflows/prod_deploy.yaml | 20 +++++++++++++++++-- .gitignore | 2 ++ .../core/management/commands/export-db.py | 2 +- .../core/management/commands/fill-db.py | 2 +- .../core/management/commands/import-db.py | 2 +- infra/prod/docker-compose.prod.yaml | 1 + 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/prod_deploy.yaml b/.github/workflows/prod_deploy.yaml index 3f8fab04..b3bd8b07 100644 --- a/.github/workflows/prod_deploy.yaml +++ b/.github/workflows/prod_deploy.yaml @@ -4,7 +4,7 @@ on: push: branches: - master - + env: DEPLOY_PATH: adaptive_hockey_federation REGISTRY: ghcr.io @@ -87,6 +87,22 @@ jobs: rm -r infra/stage rm -r infra/dev + - name: Stopping old containers + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} + script: | + STATUS="$(systemctl is-active adaptive_hockey_federation.service)" + if [ "${STATUS}" = "active" ]; then + sudo systemctl stop adaptive_hockey_federation.service + echo "Stopping old containers" + else + echo "No active containers" + fi + - name: Copy infra via ssh uses: appleboy/scp-action@master with: @@ -98,7 +114,7 @@ jobs: target: "${{ env.DEPLOY_PATH }}/infra" rm: true strip_components: 1 - + - name: Execute commands on VPS uses: appleboy/ssh-action@master with: diff --git a/.gitignore b/.gitignore index 944c5d95..5a6c1fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -184,3 +184,5 @@ media/ # DS server a_hockey-main/ adaptive_hockey_federation/service/test_video/ + +db_dump.json \ No newline at end of file diff --git a/adaptive_hockey_federation/core/management/commands/export-db.py b/adaptive_hockey_federation/core/management/commands/export-db.py index f4b88dba..bb7cb2e0 100644 --- a/adaptive_hockey_federation/core/management/commands/export-db.py +++ b/adaptive_hockey_federation/core/management/commands/export-db.py @@ -1,7 +1,7 @@ from django.core.management import call_command from django.core.management.base import BaseCommand -from adaptive_hockey_federation.core.config.dev_settings import DB_DUMP_FILE +from core.config.dev_settings import DB_DUMP_FILE class Command(BaseCommand): diff --git a/adaptive_hockey_federation/core/management/commands/fill-db.py b/adaptive_hockey_federation/core/management/commands/fill-db.py index b5ba070b..db2578b2 100644 --- a/adaptive_hockey_federation/core/management/commands/fill-db.py +++ b/adaptive_hockey_federation/core/management/commands/fill-db.py @@ -5,7 +5,7 @@ from django.db import connection, transaction from main.models import Diagnosis -from adaptive_hockey_federation.core.config.dev_settings import ( +from core.config.dev_settings import ( FILE_MODEL_MAP, FIXSTURES_DIR, ) diff --git a/adaptive_hockey_federation/core/management/commands/import-db.py b/adaptive_hockey_federation/core/management/commands/import-db.py index bff63edf..5f39118a 100644 --- a/adaptive_hockey_federation/core/management/commands/import-db.py +++ b/adaptive_hockey_federation/core/management/commands/import-db.py @@ -6,7 +6,7 @@ from django.core.management.base import BaseCommand from django.db import connection -from adaptive_hockey_federation.core.config.dev_settings import DB_DUMP_FILE +from core.config.dev_settings import DB_DUMP_FILE class Command(BaseCommand): diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 9e978a92..2a2801ba 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -20,6 +20,7 @@ services: volumes: - static_value:/app/adaptive_hockey_federation/static/ - ../../media:/app/adaptive_hockey_federation/media/ + - ../../fixtures:/app/adaptive_hockey_federation/core/fixtures/ env_file: - ../../.env depends_on: