Merge pull request 'move_webui' (#35) from move_webui into main
Code Quality Pipeline / Test (push) Successful in 4m3s
Code Quality Pipeline / Test (push) Successful in 4m3s
Reviewed-on: http://192.168.1.2:3000/brian/visual_critical_discourse_analysis/pulls/35
This commit was merged in pull request #35.
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
name: Code Quality Pipeline
|
name: Code Quality Pipeline
|
||||||
run-name: ${{ gitea.actor }} is running the Code Quality Pipeline
|
run-name: ${{ gitea.actor }} is running the Code Quality Pipeline
|
||||||
runs-on: ubuntu-latest
|
|
||||||
on: push
|
on: push
|
||||||
image: python:3.12
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
@@ -21,7 +19,7 @@ jobs:
|
|||||||
poetry install
|
poetry install
|
||||||
- name: PEP8 Check
|
- name: PEP8 Check
|
||||||
run: |
|
run: |
|
||||||
poetry run flake8 ./src --benchmark
|
poetry run flake8 . --benchmark
|
||||||
- name: Type Check
|
- name: Type Check
|
||||||
run: |
|
run: |
|
||||||
poetry run mypy ./src --disable-error-code=import-untyped
|
poetry run mypy . --disable-error-code=import-untyped
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
name: CI Pipeline
|
name: CI Pipeline
|
||||||
run-name: ${{ gitea.actor }} is running the CI Pipeline
|
run-name: ${{ gitea.actor }} is running the CI Pipeline
|
||||||
runs-on: ubuntu-latest
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
@@ -23,30 +22,52 @@ jobs:
|
|||||||
poetry install
|
poetry install
|
||||||
- name: PEP8 Check
|
- name: PEP8 Check
|
||||||
run: |
|
run: |
|
||||||
poetry run flake8 ./src --benchmark
|
poetry run flake8 . --benchmark
|
||||||
- name: Type Check
|
- name: Type Check
|
||||||
run: |
|
run: |
|
||||||
poetry run mypy ./src --disable-error-code=import-untyped
|
poetry run mypy . --disable-error-code=import-untyped
|
||||||
publish:
|
publish:
|
||||||
name: Build and Publish
|
name: Build and Publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [test]
|
needs: [test]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
-
|
||||||
|
dockerfile: ./Dockerfile.web_ui
|
||||||
|
image: ${{ vars.docker_repo_url }}/${{ gitea.repository }}/web_ui
|
||||||
|
# -
|
||||||
|
# dockerfile: ./model/Dockerfile
|
||||||
|
# image: ${{ vars.docker_repo_url }}/${{ gitea.repository }}/model
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
-
|
||||||
|
name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Set Environment Variables
|
-
|
||||||
|
name: Set Environment Variables
|
||||||
run: |
|
run: |
|
||||||
echo "sha_short=$(git rev-parse --short ${{ gitea.sha }} )" >> "$GITHUB_ENV"
|
echo "sha_short=$(git rev-parse --short ${{ gitea.sha }} )" >> "$GITHUB_ENV"
|
||||||
- name: Show Environment Variables
|
-
|
||||||
|
name: Show Environment Variables
|
||||||
run: |
|
run: |
|
||||||
echo "DOCKER_REPO_URL: ${{ vars.docker_repo_url }}"
|
echo "DOCKER_REPO_URL: ${{ vars.docker_repo_url }}"
|
||||||
echo "REPOSITORY: ${{ gitea.repository }}"
|
echo "REPOSITORY: ${{ gitea.repository }}"
|
||||||
echo "COMMIT_SHA: ${{ env.sha_short }}"
|
echo "COMMIT_SHA: ${{ env.sha_short }}"
|
||||||
- name: Build and Push Image
|
-
|
||||||
uses: docker/build-push-action@v2
|
name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ matrix.image }}
|
||||||
|
-
|
||||||
|
name: Build and Push Image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
file: ${{ matrix.dockerfile }}
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ vars.docker_repo_url }}/${{ gitea.repository }}:${{ env.sha_short }}
|
${{ matrix.image }}:${{ env.sha_short }}
|
||||||
${{ vars.docker_repo_url }}/${{ gitea.repository }}:latest
|
${{ matrix.image }}:latest
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# build stage
|
||||||
|
FROM python:3.12-slim-bookworm as BUILDER
|
||||||
|
|
||||||
|
# set environment variables
|
||||||
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PIP_NO_CACHE_DIR=off \
|
||||||
|
PIP_DISABLE_PIP_VERSION_CHECK=ON \
|
||||||
|
PIP_DEFAULT_TIMEOUT=100 \
|
||||||
|
DEBIAN_FRONTEND=noninteractive \
|
||||||
|
POETRY_HOME=/etc/poetry \
|
||||||
|
POETRY_VERSION=1.7.1 \
|
||||||
|
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
||||||
|
POETRY_VIRTUALENVS_CREATE=1 \
|
||||||
|
POETRY_NO_INTERACTION=1 \
|
||||||
|
POETRY_CACHE_DIR=/tmp/poetry_cache \
|
||||||
|
APP_HOME=/home/app
|
||||||
|
|
||||||
|
# update system
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
# install poetry
|
||||||
|
RUN curl -sSL https://install.python-poetry.org | python3 -
|
||||||
|
ENV PATH="${POETRY_HOME}/bin:$PATH"
|
||||||
|
|
||||||
|
# install runtime dependencies
|
||||||
|
WORKDIR ${APP_HOME}
|
||||||
|
COPY ./poetry.lock ./pyproject.toml ./
|
||||||
|
RUN --mount=type=cache,target=${POETRY_CACHE_DIR} poetry install \
|
||||||
|
--with shared,web_ui
|
||||||
|
|
||||||
|
# final stage
|
||||||
|
FROM python:3.12-slim-bookworm
|
||||||
|
|
||||||
|
# copy virtualenv made by poetry
|
||||||
|
ENV APP_HOME=/home/app \
|
||||||
|
VIRTUAL_ENV=/home/app/.venv
|
||||||
|
COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||||
|
ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
|
||||||
|
|
||||||
|
# create home directory and app user
|
||||||
|
RUN mkdir -p /home/app && \
|
||||||
|
addgroup --system app && \
|
||||||
|
adduser --system --group app
|
||||||
|
|
||||||
|
# add code while changing ownership
|
||||||
|
WORKDIR $APP_HOME
|
||||||
|
COPY --chown=app:app ./shared ./shared
|
||||||
|
COPY --chown=app:app ./web_ui/src ./src
|
||||||
|
|
||||||
|
# change to the app user
|
||||||
|
USER app
|
||||||
|
|
||||||
|
ENTRYPOINT [ "gunicorn", "src.main:server", "-b", "0.0.0.0:8050" ]
|
||||||
+16
-17
@@ -1,21 +1,20 @@
|
|||||||
version: '3.7'
|
|
||||||
services:
|
services:
|
||||||
# app:
|
app:
|
||||||
# image: visual_critical_discourse_analysis:dev
|
image: visual_critical_discourse_analysis:dev
|
||||||
# container_name: visual_critical_discourse_analysis
|
container_name: visual_critical_discourse_analysis
|
||||||
# build:
|
build:
|
||||||
# context: .
|
context: .
|
||||||
# dockerfile: Dockerfile
|
dockerfile: ./web_ui/Dockerfile
|
||||||
# env_file:
|
env_file:
|
||||||
# - local.env
|
- local.env
|
||||||
# environment:
|
environment:
|
||||||
# - ENV=DEV
|
- ENV=DEV
|
||||||
# ports:
|
ports:
|
||||||
# - 8050:8050
|
- 8050:8050
|
||||||
# networks:
|
networks:
|
||||||
# - backend
|
- backend
|
||||||
# depends_on:
|
depends_on:
|
||||||
# - mongo
|
- mongo
|
||||||
mongo:
|
mongo:
|
||||||
image: mongo:latest
|
image: mongo:latest
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
version: '3.7'
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: visual_critical_discourse_analysis:dev
|
image: visual_critical_discourse_analysis:dev
|
||||||
container_name: visual_critical_discourse_analysis
|
container_name: visual_critical_discourse_analysis
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: ./web_ui/Dockerfile
|
||||||
env_file:
|
env_file:
|
||||||
- server.env
|
- server.env
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import requests
|
import requests
|
||||||
|
from classes import Instagram
|
||||||
from retry import retry
|
from retry import retry
|
||||||
|
|
||||||
from image_download.classes import Instagram
|
|
||||||
|
|
||||||
|
|
||||||
def get_sources() -> pd.DataFrame:
|
def get_sources() -> pd.DataFrame:
|
||||||
"""Get sources dateframe."""
|
"""Get sources dateframe."""
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from torchvision.transforms.functional import pad
|
|||||||
from torchvision.transforms.functional import resize
|
from torchvision.transforms.functional import resize
|
||||||
from torchvision.transforms.functional import rotate
|
from torchvision.transforms.functional import rotate
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
|
|
||||||
# resnet18 original normalization values
|
# resnet18 original normalization values
|
||||||
RESNET_NORMALIZE_MEAN = [0.485, 0.456, 0.406]
|
RESNET_NORMALIZE_MEAN = [0.485, 0.456, 0.406]
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ from .point_of_view import PointOfViewTail
|
|||||||
from .resnet18_head import ResNet18Head
|
from .resnet18_head import ResNet18Head
|
||||||
from .salience import SalienceTail
|
from .salience import SalienceTail
|
||||||
from .visual_syntax import VisualSyntaxTail
|
from .visual_syntax import VisualSyntaxTail
|
||||||
from core.dto import AngleData
|
from shared.dto import AngleData
|
||||||
from core.dto import ContactData
|
from shared.dto import ContactData
|
||||||
from core.dto import DistanceData
|
from shared.dto import DistanceData
|
||||||
from core.dto import FramingData
|
from shared.dto import FramingData
|
||||||
from core.dto import InformationValueData
|
from shared.dto import InformationValueData
|
||||||
from core.dto import ModalityColorData
|
from shared.dto import ModalityColorData
|
||||||
from core.dto import ModalityDepthData
|
from shared.dto import ModalityDepthData
|
||||||
from core.dto import ModalityLightingData
|
from shared.dto import ModalityLightingData
|
||||||
from core.dto import PointOfViewData
|
from shared.dto import PointOfViewData
|
||||||
from core.dto import SalienceData
|
from shared.dto import SalienceData
|
||||||
from core.dto import VisualSyntaxData
|
from shared.dto import VisualSyntaxData
|
||||||
|
|
||||||
|
|
||||||
class VisualCommunicationModel(nn.Module):
|
class VisualCommunicationModel(nn.Module):
|
||||||
|
|||||||
Generated
+108
-104
@@ -32,13 +32,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blinker"
|
name = "blinker"
|
||||||
version = "1.8.1"
|
version = "1.8.2"
|
||||||
description = "Fast, simple object-to-object and broadcast signaling"
|
description = "Fast, simple object-to-object and broadcast signaling"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "blinker-1.8.1-py3-none-any.whl", hash = "sha256:5f1cdeff423b77c31b89de0565cd03e5275a03028f44b2b15f912632a58cced6"},
|
{file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"},
|
||||||
{file = "blinker-1.8.1.tar.gz", hash = "sha256:da44ec748222dcd0105ef975eed946da197d5bdf8bafb6aa92f5bc89da63fa25"},
|
{file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -242,13 +242,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dash"
|
name = "dash"
|
||||||
version = "2.16.1"
|
version = "2.17.0"
|
||||||
description = "A Python framework for building reactive web-apps. Developed by Plotly."
|
description = "A Python framework for building reactive web-apps. Developed by Plotly."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "dash-2.16.1-py3-none-any.whl", hash = "sha256:8a9d2a618e415113c0b2a4d25d5dc4df5cb921f733b33dde75559db2316b1df1"},
|
{file = "dash-2.17.0-py3-none-any.whl", hash = "sha256:2421569023b2cd46ea2d4b2c14fe72c71b7436527a3102219b2265fa361e7c67"},
|
||||||
{file = "dash-2.16.1.tar.gz", hash = "sha256:b2871d6b8d4c9dfd0a64f89f22d001c93292910b41d92d9ff2bb424a28283976"},
|
{file = "dash-2.17.0.tar.gz", hash = "sha256:d065cd88771e45d0485993be0d27565e08918cb7edd18e31ee1c5b41252fc2fa"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -267,7 +267,7 @@ Werkzeug = "<3.1"
|
|||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
celery = ["celery[redis] (>=5.1.2)", "redis (>=3.5.3)"]
|
celery = ["celery[redis] (>=5.1.2)", "redis (>=3.5.3)"]
|
||||||
ci = ["black (==22.3.0)", "dash-dangerously-set-inner-html", "dash-flow-example (==0.0.5)", "flake8 (==7.0.0)", "flaky (==3.7.0)", "flask-talisman (==1.0.0)", "jupyterlab (<4.0.0)", "mimesis (<=11.1.0)", "mock (==4.0.3)", "numpy (<=1.26.3)", "openpyxl", "orjson (==3.9.12)", "pandas (>=1.4.0)", "pyarrow", "pylint (==3.0.3)", "pytest-mock", "pytest-rerunfailures", "pytest-sugar (==0.9.6)", "pyzmq (==25.1.2)", "xlrd (>=2.0.1)"]
|
ci = ["black (==22.3.0)", "dash-dangerously-set-inner-html", "dash-flow-example (==0.0.5)", "flake8 (==7.0.0)", "flaky (==3.8.1)", "flask-talisman (==1.0.0)", "jupyterlab (<4.0.0)", "mimesis (<=11.1.0)", "mock (==4.0.3)", "numpy (<=1.26.3)", "openpyxl", "orjson (==3.9.12)", "pandas (>=1.4.0)", "pyarrow", "pylint (==3.0.3)", "pytest-mock", "pytest-rerunfailures", "pytest-sugar (==0.9.6)", "pyzmq (==25.1.2)", "xlrd (>=2.0.1)"]
|
||||||
compress = ["flask-compress"]
|
compress = ["flask-compress"]
|
||||||
dev = ["PyYAML (>=5.4.1)", "coloredlogs (>=15.0.1)", "fire (>=0.4.0)"]
|
dev = ["PyYAML (>=5.4.1)", "coloredlogs (>=15.0.1)", "fire (>=0.4.0)"]
|
||||||
diskcache = ["diskcache (>=5.2.1)", "multiprocess (>=0.70.12)", "psutil (>=5.8.0)"]
|
diskcache = ["diskcache (>=5.2.1)", "multiprocess (>=0.70.12)", "psutil (>=5.8.0)"]
|
||||||
@@ -440,13 +440,13 @@ dotenv = ["python-dotenv"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsspec"
|
name = "fsspec"
|
||||||
version = "2024.3.1"
|
version = "2024.5.0"
|
||||||
description = "File-system specification"
|
description = "File-system specification"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"},
|
{file = "fsspec-2024.5.0-py3-none-any.whl", hash = "sha256:e0fdbc446d67e182f49a70b82cf7889028a63588fde6b222521f10937b2b670c"},
|
||||||
{file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"},
|
{file = "fsspec-2024.5.0.tar.gz", hash = "sha256:1d021b0b0f933e3b3029ed808eb400c08ba101ca2de4b3483fbc9ca23fcee94a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@@ -454,7 +454,7 @@ abfs = ["adlfs"]
|
|||||||
adl = ["adlfs"]
|
adl = ["adlfs"]
|
||||||
arrow = ["pyarrow (>=1)"]
|
arrow = ["pyarrow (>=1)"]
|
||||||
dask = ["dask", "distributed"]
|
dask = ["dask", "distributed"]
|
||||||
devel = ["pytest", "pytest-cov"]
|
dev = ["pre-commit", "ruff"]
|
||||||
dropbox = ["dropbox", "dropboxdrivefs", "requests"]
|
dropbox = ["dropbox", "dropboxdrivefs", "requests"]
|
||||||
full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
|
full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
|
||||||
fuse = ["fusepy"]
|
fuse = ["fusepy"]
|
||||||
@@ -471,6 +471,9 @@ s3 = ["s3fs"]
|
|||||||
sftp = ["paramiko"]
|
sftp = ["paramiko"]
|
||||||
smb = ["smbprotocol"]
|
smb = ["smbprotocol"]
|
||||||
ssh = ["paramiko"]
|
ssh = ["paramiko"]
|
||||||
|
test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
|
||||||
|
test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
|
||||||
|
test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
|
||||||
tqdm = ["tqdm"]
|
tqdm = ["tqdm"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -547,13 +550,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinja2"
|
name = "jinja2"
|
||||||
version = "3.1.3"
|
version = "3.1.4"
|
||||||
description = "A very fast and expressive template engine."
|
description = "A very fast and expressive template engine."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
|
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
|
||||||
{file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
|
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1113,13 +1116,13 @@ xmp = ["defusedxml"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotly"
|
name = "plotly"
|
||||||
version = "5.21.0"
|
version = "5.22.0"
|
||||||
description = "An open-source, interactive data visualization library for Python"
|
description = "An open-source, interactive data visualization library for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "plotly-5.21.0-py3-none-any.whl", hash = "sha256:a33f41fd5922e45b2b253f795b200d14452eb625790bb72d0a72cf1328a6abbf"},
|
{file = "plotly-5.22.0-py3-none-any.whl", hash = "sha256:68fc1901f098daeb233cc3dd44ec9dc31fb3ca4f4e53189344199c43496ed006"},
|
||||||
{file = "plotly-5.21.0.tar.gz", hash = "sha256:69243f8c165d4be26c0df1c6f0b7b258e2dfeefe032763404ad7e7fb7d7c2073"},
|
{file = "plotly-5.22.0.tar.gz", hash = "sha256:859fdadbd86b5770ae2466e542b761b247d1c6b49daed765b95bb8c7063e7469"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1282,71 +1285,71 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pymongo"
|
name = "pymongo"
|
||||||
version = "4.7.1"
|
version = "4.7.2"
|
||||||
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8822614975038e0cece47d12e7634a79c2ee590a0ae78ae64c37b9c6610a14c"},
|
{file = "pymongo-4.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:268d8578c0500012140c5460755ea405cbfe541ef47c81efa9d6744f0f99aeca"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57b5b485ef89270ed2e603814f43f0fdd9b8ba5d4039124d90878cdc2327000c"},
|
{file = "pymongo-4.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:827611beb6c483260d520cfa6a49662d980dfa5368a04296f65fa39e78fccea7"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e99dac3c7c2cb498937cc1767361851099da38861e921113318c87d71e3d127"},
|
{file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a754e366c404d19ff3f077ddeed64be31e0bb515e04f502bf11987f1baa55a16"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:253ed8fd6e7f4b2a1caa89e6b287b9e04f42613319ee1e1240c2db2afe1637e7"},
|
{file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44efab10d9a3db920530f7bcb26af8f408b7273d2f0214081d3891979726328"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cee62188127a126f59ea45d3981868a5e35343be4ef4ad8712eaf42be37a00b"},
|
{file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35b3f0c7d49724859d4df5f0445818d525824a6cd55074c42573d9b50764df67"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31ed8ba3da0366346264604b3a443f5a4232cab5ed45f520bead6184cf0851a1"},
|
{file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e37faf298a37ffb3e0809e77fbbb0a32b6a2d18a83c59cfc2a7b794ea1136b0"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d043ecc0c7d5591925bbc7abb67caf21c94d6e6e5d442cb49eb5d9d8ee76b"},
|
{file = "pymongo-4.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1bcd58669e56c08f1e72c5758868b5df169fe267501c949ee83c418e9df9155"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-win32.whl", hash = "sha256:bfd5c7e5bb87171a5296fa32205adb50b27704a612036ec4395c3cd316fc0e91"},
|
{file = "pymongo-4.7.2-cp310-cp310-win32.whl", hash = "sha256:c72d16fede22efe7cdd1f422e8da15760e9498024040429362886f946c10fe95"},
|
||||||
{file = "pymongo-4.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:5ae1aeeb405c29885266666dc7115792d647ed68cfdb6ed02e2e211d12f2e1c8"},
|
{file = "pymongo-4.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:12d1fef77d25640cb78893d07ff7d2fac4c4461d8eec45bd3b9ad491a1115d6e"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e4a63ba6813a2168ebd35ea5369f6c33f7787525986cd77668b7956acc3d2a38"},
|
{file = "pymongo-4.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc5af24fcf5fc6f7f40d65446400d45dd12bea933d0299dc9e90c5b22197f1e9"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:811a07bba9d35f1e34159ede632ac71dbc429b372a20004e32d6578af872db1a"},
|
{file = "pymongo-4.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:730778b6f0964b164c187289f906bbc84cb0524df285b7a85aa355bbec43eb21"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d227555be35078b53f506f6b58bd0b0e8fd4513e89e6f29e83a97efab439250"},
|
{file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47a1a4832ef2f4346dcd1a10a36ade7367ad6905929ddb476459abb4fd1b98cb"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daf35ab13b86aba7cc8c4b019882f1fa8d287a26f586ef5eaf60a5233d3eaa52"},
|
{file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6eab12c6385526d386543d6823b07187fefba028f0da216506e00f0e1855119"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa354933a158e57494c98b592f46d5d24d1b109e6ba05a05179cde719d9f7fd3"},
|
{file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37e9ea81fa59ee9274457ed7d59b6c27f6f2a5fe8e26f184ecf58ea52a019cb8"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad360630c221aee7c0841a51851496a3ca6fdea87007098a982c1aa26e34083a"},
|
{file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9d9d2c0aae73aa4369bd373ac2ac59f02c46d4e56c4b6d6e250cfe85f76802"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5119c66af8c4197c8757b4b7d98c443e5b127c224ac92fb657dbe2b512ae2713"},
|
{file = "pymongo-4.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6e00a79dff22c9a72212ad82021b54bdb3b85f38a85f4fc466bde581d7d17a"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-win32.whl", hash = "sha256:11f74dafde63ad2dc30c01f40b4c69d9af157f8ba5224b0c9d4de7158537266f"},
|
{file = "pymongo-4.7.2-cp311-cp311-win32.whl", hash = "sha256:02efd1bb3397e24ef2af45923888b41a378ce00cb3a4259c5f4fc3c70497a22f"},
|
||||||
{file = "pymongo-4.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:ec94d29103317aa920dae59ed385de9604cb0ef840b5b7137b5eaa7a2042580a"},
|
{file = "pymongo-4.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:87bb453ac3eb44db95cb6d5a616fbc906c1c00661eec7f55696253a6245beb8a"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b8b95e2163b73d03a913efa89b0f7c5012be82efd4e9dbce8aa62010a75a277c"},
|
{file = "pymongo-4.7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:12c466e02133b7f8f4ff1045c6b5916215c5f7923bc83fd6e28e290cba18f9f6"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb1a884b1c6aeac5ffeb8ccb696fbc242a7ae1bba36f2328c01f76fab7221b94"},
|
{file = "pymongo-4.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f91073049c43d14e66696970dd708d319b86ee57ef9af359294eee072abaac79"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ccc8dd4fe9aac18dde27c33a53271c6c90159b74c43fbdab1d33d5efc36c2f5"},
|
{file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87032f818bf5052ab742812c715eff896621385c43f8f97cdd37d15b5d394e95"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7247c1dc7d8eed4e24eb1dd92c4c58ebf1e5159500015652552acfdebdeed256"},
|
{file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a87eef394039765679f75c6a47455a4030870341cb76eafc349c5944408c882"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45ac46f0d6bdc2baac34ced60aae27b2083170d77397330eff0ac5689ea29d38"},
|
{file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d275596f840018858757561840767b39272ac96436fcb54f5cac6d245393fd97"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a848249d5b4763497add62f7dd7bd0ce1538129bf42f4cb132a76d24c61bf98d"},
|
{file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82102e353be13f1a6769660dd88115b1da382447672ba1c2662a0fbe3df1d861"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ff6d56ca1f0cd3687a13ce90a32a8efb3cc3a53728e5ac160c4c30d10385a72"},
|
{file = "pymongo-4.7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194065c9d445017b3c82fb85f89aa2055464a080bde604010dc8eb932a6b3c95"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-win32.whl", hash = "sha256:e175d74c52b6c8414a4b4504a2dd42b0202d101b2eb9508a34c137357683864e"},
|
{file = "pymongo-4.7.2-cp312-cp312-win32.whl", hash = "sha256:db4380d1e69fdad1044a4b8f3bb105200542c49a0dde93452d938ff9db1d6d29"},
|
||||||
{file = "pymongo-4.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:263c169302df636f9086b584994a51d0adfc8738fe27d7b8e2aacf46fd68b6cb"},
|
{file = "pymongo-4.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:fadc6e8db7707c861ebe25b13ad6aca19ea4d2c56bf04a26691f46c23dadf6e4"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:337d99f88d32a5f8056d6d2bc365ccf09d09583f3942882c50cf11b459e8fbc0"},
|
{file = "pymongo-4.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2cb77d09bd012cb4b30636e7e38d00b5f9be5eb521c364bde66490c45ee6c4b4"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30a9d891631d7e847b24f551b1d89ff2033539e7cd8e9af29714b4d0db7abb06"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bf8b706946952acdea0fe478f8e44f1ed101c4b87f046859e6c3abe6c0a9f4"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bf96ece4999b0bbab7169cb2b9c60918b434487009e48be4bd47eeb2aa7b14"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcf337d1b252405779d9c79978d6ca15eab3cdaa2f44c100a79221bddad97c8a"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ef32a7cfe748c0c72fdad9e51459de5e0c6b16c5288b39f863abfff23503847"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ffd1519edbe311df73c74ec338de7d294af535b2748191c866ea3a7c484cd15"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c8f1dd545360ec1b79007a3ba6573af565df6fde49f6dfc53813f3f475a751"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d59776f435564159196d971aa89422ead878174aff8fe18e06d9a0bc6d648c"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b897b60b2d55c26f3efea0effc11b655db68125c3731274bc3953375e9ccab73"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:347c49cf7f0ba49ea87c1a5a1984187ecc5516b7c753f31938bf7b37462824fd"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5a58b6cd7c423ba49db10d8445756062c931ad2246ba0da1e705bf22962fd9e9"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84bc00200c3cbb6c98a2bb964c9e8284b641e4a33cf10c802390552575ee21de"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ed6b3a0740efe98bb03ccf054578e9788ebcd06d021d548b8217ab2c82e45975"},
|
{file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fcaf8c911cb29316a02356f89dbc0e0dfcc6a712ace217b6b543805690d2aefd"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-win32.whl", hash = "sha256:85b8dd3756b73993b1e3ab6b1cba826b9e4987a094a5d5b6d37313776458cd94"},
|
{file = "pymongo-4.7.2-cp37-cp37m-win32.whl", hash = "sha256:b48a5650ee5320d59f6d570bd99a8d5c58ac6f297a4e9090535f6561469ac32e"},
|
||||||
{file = "pymongo-4.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:297cdc87c4b4168782b571c8643540e9b0ad1d09266b43d2f5954f8632280835"},
|
{file = "pymongo-4.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5239ef7e749f1326ea7564428bf861d5250aa39d7f26d612741b1b1273227062"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7b10603ba64af08f5af7eb9a69d6b24e3c69d91fdd48c54b95e284686c1c582d"},
|
{file = "pymongo-4.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2dcf608d35644e8d276d61bf40a93339d8d66a0e5f3e3f75b2c155a421a1b71"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64b69b9cd8a6d23881a80490d575e92918f9afca43096a7d6c1013d6b3e5c75c"},
|
{file = "pymongo-4.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25eeb2c18ede63891cbd617943dd9e6b9cbccc54f276e0b2e693a0cc40f243c5"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c7e05454cdc5aa4702e03cad0df4205daccd6fd631bbbf0a85bbe598129a6cc"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9349f0bb17a31371d4cacb64b306e4ca90413a3ad1fffe73ac7cd495570d94b5"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e0a30a022ac8a9164ee5a4b761e13dbb3d10a21845f7258011e3415151fb645"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd4d7cb2e6c6e100e2b39606d38a9ffc934e18593dc9bb326196afc7d93ce3d"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13fc201e073644acd77860d9e91ccfc27addf510563e07381cadc9a55ac3a894"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a8bd37f5dabc86efceb8d8cbff5969256523d42d08088f098753dba15f3b37a"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd998e9f0f7694032c1648c7f57fccaa78903df6329b8f8ae20cfa7c4ceca34"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c78f156edc59b905c80c9003e022e1a764c54fd40ac4fea05b0764f829790e2"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455f9d603ed0990a787773d5718e871300bddf585ce543baf129c9f5ca3adb02"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d892fb91e81cccb83f507cdb2ea0aa026ec3ced7f12a1d60f6a5bf0f20f9c1f"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d804eaf19a65211cc2c8c5db75be685c3f31c64cdab639794f66f13f8e258ba6"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87832d6076c2c82f42870157414fd876facbb6554d2faf271ffe7f8f30ce7bed"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a46c08ef0b273c415b1e8933f6739596be264ae700a4927f84e0b84e70fdf0eb"},
|
{file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ce1a374ea0e49808e0380ffc64284c0ce0f12bd21042b4bef1af3eb7bdf49054"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-win32.whl", hash = "sha256:58989bcb94233233a71645236b972835d4f87a6bb1b7e818d38a7e6e6d4630de"},
|
{file = "pymongo-4.7.2-cp38-cp38-win32.whl", hash = "sha256:eb0642e5f0dd7e86bb358749cc278e70b911e617f519989d346f742dc9520dfb"},
|
||||||
{file = "pymongo-4.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:d63f38454a2e23c117d3ceab3b661568f2418536825787256ad24e5baaedfd27"},
|
{file = "pymongo-4.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:4bdb5ffe1cd3728c9479671a067ef44dacafc3743741d4dc700c377c4231356f"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d50969de00d3522b2c394f7e59b843871e2be4b525af92066da7f3bd02799fdc"},
|
{file = "pymongo-4.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:743552033c63f0afdb56b9189ab04b5c1dbffd7310cf7156ab98eebcecf24621"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f2a720e787c9b9b912db5bb4c3e7123ccff1352d6c3ac0cb2c7ee392cdc95c00"},
|
{file = "pymongo-4.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5239776633f7578b81207e5646245415a5a95f6ae5ef5dff8e7c2357e6264bfc"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c808098f2cdb87d4035144e536ba5fa7709d0420c17b68e6ace5da18c38ded5f"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727ad07952c155cd20045f2ce91143c7dc4fb01a5b4e8012905a89a7da554b0c"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1829a7db720ff586aaf59c806e89e0a388548063aa844d21a570a231ad8ca87"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9385654f01a90f73827af4db90c290a1519f7d9102ba43286e187b373e9a78e9"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:615c7573d7a9c4837332a673fdc5a5f214b474dd52d846bcf4cc3d011550bee1"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d833651f1ba938bb7501f13e326b96cfbb7d98867b2d545ca6d69c7664903e0"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e935712b17e7a42831022353bac91a346a792658a54e12bec907ec11695cc899"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf17ea9cea14d59b0527403dd7106362917ced7c4ec936c4ba22bd36c912c8e0"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc32217c81d87750401fa1c2bc9450e854b23e6e30243c82d3514b8e58f39e3"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cecd2df037249d1c74f0af86fb5b766104a5012becac6ff63d85d1de53ba8b98"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bc87db2e9563295c4e45602ab978a2fcbaba3ab89e745503b24f895cddeb755"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65b4c00dedbd333698b83cd2095a639a6f0d7c4e2a617988f6c65fb46711f028"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:65c45682d5ed8c6618bde22cd6716b47a197f4ef800a025213b28d13a59e5fca"},
|
{file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9b6cbc037108ff1a0a867e7670d8513c37f9bcd9ee3d2464411bfabf70ca002"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-win32.whl", hash = "sha256:67cbee427c263a4483e3249fef480788ccc16edb1a4fc330c4c6cb0cb9db94a8"},
|
{file = "pymongo-4.7.2-cp39-cp39-win32.whl", hash = "sha256:cf28430ec1924af1bffed37b69a812339084697fd3f3e781074a0148e6475803"},
|
||||||
{file = "pymongo-4.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bd1eef70c1eda838b26397ef75c9580d7a97fd94b6324971d7f3d2ad3552e9a"},
|
{file = "pymongo-4.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:e004527ea42a6b99a8b8d5b42b42762c3bdf80f88fbdb5c3a9d47f3808495b86"},
|
||||||
{file = "pymongo-4.7.1.tar.gz", hash = "sha256:811c41c6227b7548afcb53e1b996c25262d837b5e5f519e2ddc2c7e59d8728a5"},
|
{file = "pymongo-4.7.2.tar.gz", hash = "sha256:9024e1661c6e40acf468177bf90ce924d1bc681d2b244adda3ed7b2f4c4d17d7"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1414,13 +1417,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.31.0"
|
version = "2.32.0"
|
||||||
description = "Python HTTP for Humans."
|
description = "Python HTTP for Humans."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
{file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"},
|
||||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
{file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1464,13 +1467,13 @@ six = ">=1.7.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selenium"
|
name = "selenium"
|
||||||
version = "4.20.0"
|
version = "4.21.0"
|
||||||
description = ""
|
description = ""
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "selenium-4.20.0-py3-none-any.whl", hash = "sha256:b1d0c33b38ca27d0499183e48e1dd09ff26973481f5d3ef2983073813ae6588d"},
|
{file = "selenium-4.21.0-py3-none-any.whl", hash = "sha256:4770ffe5a5264e609de7dc914be6b89987512040d5a8efb2abb181330d097993"},
|
||||||
{file = "selenium-4.20.0.tar.gz", hash = "sha256:0bd564ee166980d419a8aaf4ac00289bc152afcf2eadca5efe8c8e36711853fd"},
|
{file = "selenium-4.21.0.tar.gz", hash = "sha256:650dbfa5159895ff00ad16e5ddb6ceecb86b90c7ed2012b3f041f64e6e4904fe"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1545,17 +1548,18 @@ mpmath = ">=0.19"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tenacity"
|
name = "tenacity"
|
||||||
version = "8.2.3"
|
version = "8.3.0"
|
||||||
description = "Retry code until it succeeds"
|
description = "Retry code until it succeeds"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
|
{file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"},
|
||||||
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
|
{file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
doc = ["reno", "sphinx"]
|
||||||
|
test = ["pytest", "tornado (>=4.5)", "typeguard"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "torch"
|
name = "torch"
|
||||||
@@ -1658,13 +1662,13 @@ scipy = ["scipy"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trio"
|
name = "trio"
|
||||||
version = "0.25.0"
|
version = "0.25.1"
|
||||||
description = "A friendly Python library for async concurrency and I/O"
|
description = "A friendly Python library for async concurrency and I/O"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "trio-0.25.0-py3-none-any.whl", hash = "sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81"},
|
{file = "trio-0.25.1-py3-none-any.whl", hash = "sha256:e42617ba091e7b2e50c899052e83a3c403101841de925187f61e7b7eaebdf3fb"},
|
||||||
{file = "trio-0.25.0.tar.gz", hash = "sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e"},
|
{file = "trio-0.25.1.tar.gz", hash = "sha256:9f5314f014ea3af489e77b001861c535005c3858d38ec46b6b071ebfa339d7fb"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1692,13 +1696,13 @@ wsproto = ">=0.14"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-pillow"
|
name = "types-pillow"
|
||||||
version = "10.2.0.20240423"
|
version = "10.2.0.20240520"
|
||||||
description = "Typing stubs for Pillow"
|
description = "Typing stubs for Pillow"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "types-Pillow-10.2.0.20240423.tar.gz", hash = "sha256:696e68b9b6a58548fc307a8669830469237c5b11809ddf978ac77fafa79251cd"},
|
{file = "types-Pillow-10.2.0.20240520.tar.gz", hash = "sha256:130b979195465fa1e1676d8e81c9c7c30319e8e95b12fae945e8f0d525213107"},
|
||||||
{file = "types_Pillow-10.2.0.20240423-py3-none-any.whl", hash = "sha256:bd12923093b96c91d523efcdb66967a307f1a843bcfaf2d5a529146c10a9ced3"},
|
{file = "types_Pillow-10.2.0.20240520-py3-none-any.whl", hash = "sha256:33c36494b380e2a269bb742181bea5d9b00820367822dbd3760f07210a1da23d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1761,13 +1765,13 @@ requests = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "werkzeug"
|
name = "werkzeug"
|
||||||
version = "3.0.2"
|
version = "3.0.3"
|
||||||
description = "The comprehensive WSGI web application library."
|
description = "The comprehensive WSGI web application library."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
|
{file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"},
|
||||||
{file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
|
{file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1792,20 +1796,20 @@ h11 = ">=0.9.0,<1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zipp"
|
name = "zipp"
|
||||||
version = "3.18.1"
|
version = "3.18.2"
|
||||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
|
{file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"},
|
||||||
{file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
|
{file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "6c4bb66c774e93a08e4180f3148942edf91e2b5620ea1f6c3e4c30045bb643e5"
|
content-hash = "2db221a28d46b210a8fa0b78ad2fa3ac606a52052ad5e02b50d5b07db1e1c5b7"
|
||||||
|
|||||||
+15
-10
@@ -5,20 +5,11 @@ description = ""
|
|||||||
authors = ["Brian Bjarke Jensen <[email protected]>"]
|
authors = ["Brian Bjarke Jensen <[email protected]>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
packages = [
|
packages = [
|
||||||
{ include = "core" },
|
{ include = "shared" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.12"
|
python = "^3.12"
|
||||||
gunicorn = "^21.2.0"
|
|
||||||
python-dotenv = "^1.0.1"
|
|
||||||
dash = "^2.15.0"
|
|
||||||
dash-bootstrap-components = "^1.5.0"
|
|
||||||
dash-mantine-components = "^0.12.1"
|
|
||||||
pydantic = "^2.6.1"
|
|
||||||
pillow = "^10.2.0"
|
|
||||||
pymongo = "^4.6.1"
|
|
||||||
dash-auth = "^2.2.0"
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
@@ -38,6 +29,20 @@ retry = "^0.9.2"
|
|||||||
torch = "^2.2.1"
|
torch = "^2.2.1"
|
||||||
torchvision = "^0.17.1"
|
torchvision = "^0.17.1"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.group.shared.dependencies]
|
||||||
|
pymongo = "^4.7.2"
|
||||||
|
pydantic = "^2.6.1"
|
||||||
|
pillow = "^10.2.0"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.group.web_ui.dependencies]
|
||||||
|
dash = "^2.17.0"
|
||||||
|
gunicorn = "^21.2.0"
|
||||||
|
dash-bootstrap-components = "^1.5.0"
|
||||||
|
dash-mantine-components = "^0.12.1"
|
||||||
|
dash-auth = "^2.2.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ from pydantic import BaseModel
|
|||||||
from pydantic import field_serializer
|
from pydantic import field_serializer
|
||||||
from pydantic import field_validator
|
from pydantic import field_validator
|
||||||
|
|
||||||
from core.dto import ModelData
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
class VisualCommunication(BaseModel):
|
class VisualCommunication(BaseModel):
|
||||||
+2
-2
@@ -5,8 +5,8 @@ import logging
|
|||||||
|
|
||||||
from pymongo.collection import Collection
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
from core.database import NoDocumentFoundException
|
from shared.database import NoDocumentFoundException
|
||||||
from core.database import VisualCommunication
|
from shared.database import VisualCommunication
|
||||||
|
|
||||||
|
|
||||||
def get_visual_communication(
|
def get_visual_communication(
|
||||||
@@ -4,7 +4,7 @@ import logging
|
|||||||
|
|
||||||
from pymongo.collection import Collection
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
from core.database import Dataset
|
from shared.database import Dataset
|
||||||
|
|
||||||
|
|
||||||
def save_dataset(
|
def save_dataset(
|
||||||
@@ -21,7 +21,7 @@ def save_dataset(
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
load_dotenv('local.env')
|
load_dotenv('local.env')
|
||||||
from core.database import list_names, connect
|
from shared.database import list_names, connect
|
||||||
# connect to database
|
# connect to database
|
||||||
collection, db, client = connect()
|
collection, db, client = connect()
|
||||||
print(client.server_info())
|
print(client.server_info())
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import logging
|
|||||||
|
|
||||||
from pymongo.collection import Collection
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
from core.dto import ModelData
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
def upsert_annotation(
|
def upsert_annotation(
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import logging
|
|||||||
|
|
||||||
from pymongo.collection import Collection
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
from core.dto import ModelData
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
def upsert_prediction(
|
def upsert_prediction(
|
||||||
+1
-1
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pymongo.collection import Collection
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
from core.database import VisualCommunication
|
from shared.database import VisualCommunication
|
||||||
|
|
||||||
|
|
||||||
def upsert_visual_communication(
|
def upsert_visual_communication(
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .check_env import check_env
|
||||||
|
from .setup_logging import setup_logging
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"""Definition of check_env function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def check_env() -> None:
|
||||||
|
"""Check necessary environment variables are set."""
|
||||||
|
necesasary_var_list = {
|
||||||
|
'MONGO_HOST',
|
||||||
|
'MONGO_DB',
|
||||||
|
'MONGO_COLLECTION',
|
||||||
|
'MONGO_USER',
|
||||||
|
'MONGO_PASSWORD',
|
||||||
|
'DASH_AUTH_USERNAME',
|
||||||
|
'DASH_AUTH_PASSWORD',
|
||||||
|
}
|
||||||
|
for env_var in necesasary_var_list:
|
||||||
|
# ensure env var set
|
||||||
|
assert (
|
||||||
|
env_var in os.environ
|
||||||
|
), (
|
||||||
|
f"environment variable not set: {env_var}"
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
"""Definition of setup_logging function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging() -> None:
|
||||||
|
"""Setup logging."""
|
||||||
|
requested_log_level = os.getenv('LOG_LEVEL', default='info')
|
||||||
|
level = getattr(logging, requested_log_level.upper())
|
||||||
|
fmt = (
|
||||||
|
'%(asctime)s | '
|
||||||
|
'%(levelname)s | '
|
||||||
|
'%(filename)s | '
|
||||||
|
'%(funcName)s | '
|
||||||
|
'%(message)s'
|
||||||
|
)
|
||||||
|
datefmt = '%Y-%m-%d %H:%M:%S'
|
||||||
|
logging.basicConfig(format=fmt, datefmt=datefmt, level=level)
|
||||||
|
logging.debug('finished')
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
from src.web import app
|
|
||||||
|
|
||||||
# prepare optional local setup
|
|
||||||
env_path = Path(__file__).parent.parent / 'local.env'
|
|
||||||
load_dotenv(env_path)
|
|
||||||
|
|
||||||
# ensure env vars set
|
|
||||||
necesasary_var_list = {
|
|
||||||
'MONGO_HOST',
|
|
||||||
'MONGO_DB',
|
|
||||||
'MONGO_COLLECTION',
|
|
||||||
'MONGO_USER',
|
|
||||||
'MONGO_PASSWORD',
|
|
||||||
'DASH_AUTH_USERNAME',
|
|
||||||
'DASH_AUTH_PASSWORD',
|
|
||||||
}
|
|
||||||
for env_var in necesasary_var_list:
|
|
||||||
# ensure env var set
|
|
||||||
assert (
|
|
||||||
env_var in os.environ
|
|
||||||
), (
|
|
||||||
f"environment variable not set: {env_var}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# setup logging stream handler
|
|
||||||
fmt = (
|
|
||||||
'%(asctime)s | '
|
|
||||||
'%(levelname)s | '
|
|
||||||
'%(filename)s | '
|
|
||||||
'%(funcName)s | '
|
|
||||||
'%(message)s'
|
|
||||||
)
|
|
||||||
datefmt = '%Y-%m-%d %H:%M:%S'
|
|
||||||
logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO)
|
|
||||||
|
|
||||||
logging.info('initialized app')
|
|
||||||
server = app.server
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# prepare local env vars
|
|
||||||
os.environ['MONGO_HOST'] = 'localhost'
|
|
||||||
# run app
|
|
||||||
app.run(debug=True)
|
|
||||||
logging.info('started app')
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from .classes import VisualSyntaxModelOutput
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
from pydantic import BaseModel, ValidationError
|
|
||||||
from typing import List
|
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
class OptionNotSetException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ModelOutput(BaseModel):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def classname(cls) -> str:
|
|
||||||
"""Return classname."""
|
|
||||||
return cls.__name__
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def list_fields(cls) -> List[str]:
|
|
||||||
"""List options that are stored as attributes."""
|
|
||||||
return list(cls.model_fields.keys())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_random(cls):
|
|
||||||
"""Instantiate with random numbers."""
|
|
||||||
kwargs = {field: random.random() for field in cls.list_fields()}
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_choice(cls, option: str):
|
|
||||||
"""Instantiate from choice."""
|
|
||||||
if option is None:
|
|
||||||
raise ValidationError()
|
|
||||||
assert isinstance(option, str), "option is not a string"
|
|
||||||
allowed_options_list = cls.list_fields()
|
|
||||||
assert option in allowed_options_list, \
|
|
||||||
f"{option} is not among allowed fields {allowed_options_list}"
|
|
||||||
kwargs = {field: 0 for field in cls.list_fields()}
|
|
||||||
kwargs[option] = 1
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
model_repr_str = f"{self.classname()}("
|
|
||||||
model_repr_str += ", ".join([
|
|
||||||
f"{field}={value:.3f}"
|
|
||||||
for field, value
|
|
||||||
in model_dict.items()
|
|
||||||
])
|
|
||||||
model_repr_str += ")"
|
|
||||||
return model_repr_str
|
|
||||||
|
|
||||||
def highest_score_field(self) -> str:
|
|
||||||
"""Return name of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict, key=lambda k: model_dict[k])
|
|
||||||
|
|
||||||
def highest_score_value(self) -> float:
|
|
||||||
"""Return value of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict.values())
|
|
||||||
|
|
||||||
|
|
||||||
class VisualSyntaxModelOutput(ModelOutput):
|
|
||||||
non_transactional_action: float
|
|
||||||
non_transactional_reaction: float
|
|
||||||
unidirectional_transactional_action: float
|
|
||||||
unidirectional_transactional_reaction: float
|
|
||||||
bidirectional_transactional_action: float
|
|
||||||
bidirectional_transactional_reaction: float
|
|
||||||
conversion: float
|
|
||||||
speech_process: float
|
|
||||||
classification_overt_taxonomy: float
|
|
||||||
analytical_exhaustive: float
|
|
||||||
analytical_disarranged: float
|
|
||||||
analytical_temporal: float
|
|
||||||
analytical_distributed: float
|
|
||||||
analytical_topological: float
|
|
||||||
analytical_exploded: float
|
|
||||||
analytical_inclusive: float
|
|
||||||
symbolic_suggestive: float
|
|
||||||
symbolic_attributive: float
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
m = VisualSyntaxModelOutput.from_random()
|
|
||||||
print(m)
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
CLASS_NAMES = [
|
|
||||||
"non transactional action",
|
|
||||||
"non transactional reaction",
|
|
||||||
"unidirectional transactional action",
|
|
||||||
"unidirectional transactional reaction",
|
|
||||||
"bidirectional transactional action",
|
|
||||||
"bidirectional transactional reaction",
|
|
||||||
"conversion",
|
|
||||||
"speech process",
|
|
||||||
"classification overt taxonomy",
|
|
||||||
"analytical exhaustive",
|
|
||||||
"analytical disarranged",
|
|
||||||
"analytical temporal",
|
|
||||||
"analytical distributed",
|
|
||||||
"anaytical topological",
|
|
||||||
"analytical exploded",
|
|
||||||
"analytical inclusive",
|
|
||||||
"symbolic suggestive",
|
|
||||||
"symbolic attributive"
|
|
||||||
]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
from .classes import (
|
|
||||||
ContactModelOutput,
|
|
||||||
AngleModelOutput,
|
|
||||||
PointOfViewModelOutput,
|
|
||||||
DistanceModelOutput,
|
|
||||||
ModalityLightingModelOutput,
|
|
||||||
ModalityColorModelOutput,
|
|
||||||
ModalityDepthModelOutput
|
|
||||||
)
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
from pydantic import BaseModel, ValidationError
|
|
||||||
from typing import List
|
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
class ModelOutput(BaseModel):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def classname(cls) -> str:
|
|
||||||
"""Return classname."""
|
|
||||||
return cls.__name__
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def list_fields(cls) -> List[str]:
|
|
||||||
"""List options that are stored as attributes."""
|
|
||||||
return list(cls.model_fields.keys())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_random(cls):
|
|
||||||
"""Instantiate with random numbers."""
|
|
||||||
kwargs = {field: random.random() for field in cls.list_fields()}
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_choice(cls, option: str):
|
|
||||||
"""Instantiate from choice."""
|
|
||||||
if option is None:
|
|
||||||
raise ValidationError()
|
|
||||||
assert isinstance(option, str)
|
|
||||||
allowed_options_list = cls.list_fields()
|
|
||||||
assert option in allowed_options_list, \
|
|
||||||
f"{option} is not among allowed fields {allowed_options_list}"
|
|
||||||
kwargs = {field: 0 for field in cls.list_fields()}
|
|
||||||
kwargs[option] = 1
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
model_repr_str = f"{self.classname()}("
|
|
||||||
model_repr_str += ", ".join([
|
|
||||||
f"{field}={value:.3f}"
|
|
||||||
for field, value
|
|
||||||
in model_dict.items()
|
|
||||||
])
|
|
||||||
model_repr_str += ")"
|
|
||||||
return model_repr_str
|
|
||||||
|
|
||||||
def highest_score_field(self) -> str:
|
|
||||||
"""Return name of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict, key=lambda k: model_dict[k])
|
|
||||||
|
|
||||||
def highest_score_value(self) -> float:
|
|
||||||
"""Return value of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict.values())
|
|
||||||
|
|
||||||
|
|
||||||
class ContactModelOutput(ModelOutput):
|
|
||||||
offer: float
|
|
||||||
demand: float
|
|
||||||
|
|
||||||
|
|
||||||
class AngleModelOutput(ModelOutput):
|
|
||||||
high: float
|
|
||||||
eye_level: float
|
|
||||||
low: float
|
|
||||||
|
|
||||||
|
|
||||||
class PointOfViewModelOutput(ModelOutput):
|
|
||||||
frontal: float
|
|
||||||
oblique: float
|
|
||||||
|
|
||||||
|
|
||||||
class DistanceModelOutput(ModelOutput):
|
|
||||||
long: float
|
|
||||||
medium: float
|
|
||||||
close: float
|
|
||||||
|
|
||||||
|
|
||||||
class ModalityLightingModelOutput(ModelOutput):
|
|
||||||
high: float
|
|
||||||
medium: float
|
|
||||||
low: float
|
|
||||||
|
|
||||||
|
|
||||||
class ModalityColorModelOutput(ModelOutput):
|
|
||||||
high: float
|
|
||||||
medium: float
|
|
||||||
low: float
|
|
||||||
|
|
||||||
|
|
||||||
class ModalityDepthModelOutput(ModelOutput):
|
|
||||||
high: float
|
|
||||||
medium: float
|
|
||||||
low: float
|
|
||||||
|
|
||||||
|
|
||||||
# class InterpersonalModelOutput(BaseModel):
|
|
||||||
# contact: ContactModelOutput
|
|
||||||
# angle: AngleModelOutput
|
|
||||||
# point_of_view: PointOfViewModelOutput
|
|
||||||
# distance: DistanceModelOutput
|
|
||||||
# modality_lighting: ModalityLightingModelOutput
|
|
||||||
# modality_color: ModalityColorModelOutput
|
|
||||||
# modality_depth: ModalityDepthModelOutput
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
m = ContactModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = AngleModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = PointOfViewModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = DistanceModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = ModalityLightingModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = ModalityColorModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = ModalityDepthModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
model_labels = {
|
|
||||||
"contact": [
|
|
||||||
"offer",
|
|
||||||
"demand"
|
|
||||||
],
|
|
||||||
"angle": [
|
|
||||||
"high",
|
|
||||||
"eye-level",
|
|
||||||
"low"
|
|
||||||
],
|
|
||||||
"point-of-view": [
|
|
||||||
"frontal",
|
|
||||||
"oblique"
|
|
||||||
],
|
|
||||||
"distance": [
|
|
||||||
"long",
|
|
||||||
"medium",
|
|
||||||
"close"
|
|
||||||
],
|
|
||||||
"modality lighting": [
|
|
||||||
"high",
|
|
||||||
"medium",
|
|
||||||
"low"
|
|
||||||
],
|
|
||||||
"modality color": [
|
|
||||||
"high",
|
|
||||||
"medium",
|
|
||||||
"low"
|
|
||||||
],
|
|
||||||
"modality depth": [
|
|
||||||
"high",
|
|
||||||
"medium",
|
|
||||||
"low"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
from .classes import (
|
|
||||||
InformationValueModelOutput,
|
|
||||||
FramingModelOutput,
|
|
||||||
SalienceModelOutput
|
|
||||||
)
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
from pydantic import BaseModel, ValidationError
|
|
||||||
from typing import List
|
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
class ModelOutput(BaseModel):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def classname(cls) -> str:
|
|
||||||
"""Return classname."""
|
|
||||||
return cls.__name__
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def list_fields(cls) -> List[str]:
|
|
||||||
"""List options that are stored as attributes."""
|
|
||||||
return list(cls.model_fields.keys())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_random(cls):
|
|
||||||
"""Instantiate with random numbers."""
|
|
||||||
kwargs = {field: random.random() for field in cls.list_fields()}
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_choice(cls, option: str):
|
|
||||||
"""Instantiate from choice."""
|
|
||||||
if option is None:
|
|
||||||
raise ValidationError()
|
|
||||||
assert isinstance(option, str)
|
|
||||||
allowed_options_list = cls.list_fields()
|
|
||||||
assert option in allowed_options_list, \
|
|
||||||
f"{option} is not among allowed fields {allowed_options_list}"
|
|
||||||
kwargs = {field: 0 for field in cls.list_fields()}
|
|
||||||
kwargs[option] = 1
|
|
||||||
return cls(**kwargs)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
model_repr_str = f"{self.classname()}("
|
|
||||||
model_repr_str += ", ".join([
|
|
||||||
f"{field}={value:.3f}"
|
|
||||||
for field, value
|
|
||||||
in model_dict.items()
|
|
||||||
])
|
|
||||||
model_repr_str += ")"
|
|
||||||
return model_repr_str
|
|
||||||
|
|
||||||
def highest_score_field(self) -> str:
|
|
||||||
"""Return name of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict, key=lambda k: model_dict[k])
|
|
||||||
|
|
||||||
def highest_score_value(self) -> float:
|
|
||||||
"""Return value of field with highest score."""
|
|
||||||
model_dict = self.model_dump()
|
|
||||||
return max(model_dict.values())
|
|
||||||
|
|
||||||
|
|
||||||
class InformationValueModelOutput(ModelOutput):
|
|
||||||
given_new: float
|
|
||||||
ideal_real: float
|
|
||||||
central_marginal: float
|
|
||||||
|
|
||||||
|
|
||||||
class FramingModelOutput(ModelOutput):
|
|
||||||
frame_lines: float
|
|
||||||
empty_space: float
|
|
||||||
colour_contrast: float
|
|
||||||
form_contrast: float
|
|
||||||
|
|
||||||
|
|
||||||
class SalienceModelOutput(ModelOutput):
|
|
||||||
size: float
|
|
||||||
colour: float
|
|
||||||
tone: float
|
|
||||||
form: float
|
|
||||||
positioning: float
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
m = InformationValueModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = FramingModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
m = SalienceModelOutput.from_random()
|
|
||||||
print(repr(m))
|
|
||||||
print(m.highest_score_field())
|
|
||||||
print(m.highest_score_value())
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
model_labels = {
|
|
||||||
"information value": [
|
|
||||||
"given-new",
|
|
||||||
"ideal-real",
|
|
||||||
"central-marginal"
|
|
||||||
],
|
|
||||||
"framing": [
|
|
||||||
"frame lines",
|
|
||||||
"empty space",
|
|
||||||
"colour contrast",
|
|
||||||
"form contrast"
|
|
||||||
],
|
|
||||||
"salience": [
|
|
||||||
"size",
|
|
||||||
"colour",
|
|
||||||
"tone",
|
|
||||||
"form",
|
|
||||||
"positioning"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from src.web.app import app
|
|
||||||
from src.web.app import server
|
|
||||||
-247
@@ -1,247 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import dash_bootstrap_components as dbc
|
|
||||||
from dash import ALL
|
|
||||||
from dash import Dash
|
|
||||||
from dash import Input
|
|
||||||
from dash import Output
|
|
||||||
from dash import State
|
|
||||||
from dash_auth import BasicAuth
|
|
||||||
from pydantic import ValidationError
|
|
||||||
|
|
||||||
from .layout import app_layout
|
|
||||||
from core.database import connect
|
|
||||||
from core.database import count_documents
|
|
||||||
from core.database import get_visual_communication
|
|
||||||
from core.database import NoDocumentFoundException
|
|
||||||
from core.database import upsert_annotation
|
|
||||||
from core.database import upsert_visual_communication
|
|
||||||
from core.database import VisualCommunication
|
|
||||||
from core.dto import ModelData
|
|
||||||
|
|
||||||
# setup app
|
|
||||||
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
|
||||||
app.title = 'visual critical discourse analysis'.title()
|
|
||||||
app.layout = app_layout
|
|
||||||
server = app.server
|
|
||||||
|
|
||||||
# setup authentication
|
|
||||||
AUTH_DICT = {
|
|
||||||
os.getenv('DASH_AUTH_USERNAME'): os.getenv('DASH_AUTH_PASSWORD'),
|
|
||||||
}
|
|
||||||
BasicAuth(app, AUTH_DICT)
|
|
||||||
|
|
||||||
# connect to database
|
|
||||||
collection, db, client = connect()
|
|
||||||
|
|
||||||
|
|
||||||
# define callbacks
|
|
||||||
@app.callback(
|
|
||||||
Output('alert-element', 'is_open'),
|
|
||||||
Output('alert-element', 'children'),
|
|
||||||
Input('alert-message', 'data'),
|
|
||||||
)
|
|
||||||
def show_alert(
|
|
||||||
msg: str | None,
|
|
||||||
):
|
|
||||||
if msg is None or msg == '':
|
|
||||||
return False, ''
|
|
||||||
logging.info(f'updated alert message: {msg}')
|
|
||||||
return True, msg
|
|
||||||
|
|
||||||
|
|
||||||
@app.callback(
|
|
||||||
Output('success-element', 'is_open'),
|
|
||||||
Output('success-element', 'children'),
|
|
||||||
Input('success-message', 'data'),
|
|
||||||
)
|
|
||||||
def show_success(
|
|
||||||
msg: str | None,
|
|
||||||
):
|
|
||||||
if msg is None or msg == '':
|
|
||||||
return False, ''
|
|
||||||
logging.info(f"updated success message: {msg}")
|
|
||||||
return True, msg
|
|
||||||
|
|
||||||
|
|
||||||
@app.callback(
|
|
||||||
Output('progress-bar', 'value'),
|
|
||||||
Output('progress-bar', 'max'),
|
|
||||||
Output('progress-bar', 'label'),
|
|
||||||
Input('next-button', 'n_clicks'),
|
|
||||||
Input('upload-data', 'filename'),
|
|
||||||
)
|
|
||||||
def update_progress_bar(
|
|
||||||
n_clicks: int,
|
|
||||||
filename_list: list[str] | None,
|
|
||||||
):
|
|
||||||
logging.info('began updating progress bar')
|
|
||||||
global collection
|
|
||||||
# get values from database
|
|
||||||
num_total = count_documents(
|
|
||||||
collection=collection,
|
|
||||||
only_with_annotation=False,
|
|
||||||
)
|
|
||||||
num_handled = count_documents(
|
|
||||||
collection=collection,
|
|
||||||
only_with_annotation=True,
|
|
||||||
)
|
|
||||||
limit = int(num_total/20)
|
|
||||||
label_str = f"{num_handled}/{num_total}" if num_handled >= limit else ''
|
|
||||||
return num_handled, num_total, label_str
|
|
||||||
|
|
||||||
|
|
||||||
@app.callback(
|
|
||||||
Output('success-message', 'data', allow_duplicate=True),
|
|
||||||
Output('alert-message', 'data', allow_duplicate=True),
|
|
||||||
Input('upload-data', 'contents'),
|
|
||||||
State('upload-data', 'filename'),
|
|
||||||
prevent_initial_call=True,
|
|
||||||
)
|
|
||||||
def upload_images(
|
|
||||||
content_list: list[str] | None,
|
|
||||||
filename_list: list[str] | None,
|
|
||||||
):
|
|
||||||
logging.info('began uploading images')
|
|
||||||
global collection
|
|
||||||
# stop early if possible
|
|
||||||
if content_list is None or filename_list is None:
|
|
||||||
logging.warning('content_list is None -> nothing to upload')
|
|
||||||
return None, 'nothing selected to upload'.title()
|
|
||||||
# build list of visual communication
|
|
||||||
visual_communication_list = []
|
|
||||||
for content, filename in zip(content_list, filename_list):
|
|
||||||
try:
|
|
||||||
image = VisualCommunication.decode_image(content)
|
|
||||||
vis_com = VisualCommunication(
|
|
||||||
name=filename,
|
|
||||||
image=image,
|
|
||||||
)
|
|
||||||
except Exception as exc:
|
|
||||||
logging.warning(
|
|
||||||
(
|
|
||||||
'failed creating VisualCommunication object '
|
|
||||||
f"from file {filename}"
|
|
||||||
),
|
|
||||||
exc,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
visual_communication_list.append(vis_com)
|
|
||||||
# upsert documents
|
|
||||||
success = upsert_visual_communication(
|
|
||||||
collection=collection,
|
|
||||||
visual_communication_list=visual_communication_list,
|
|
||||||
)
|
|
||||||
if success:
|
|
||||||
logging.info(f"succesfully uploaded {len(filename_list)} images")
|
|
||||||
return 'successfully uploaded images'.title(), None
|
|
||||||
logging.warning('failed inserting images into database')
|
|
||||||
return None, 'failed uploading images'.title()
|
|
||||||
|
|
||||||
|
|
||||||
@app.callback(
|
|
||||||
Output('alert-message', 'data', allow_duplicate=True),
|
|
||||||
Output('vis-com-name', 'data'),
|
|
||||||
Output('image-container', 'src'),
|
|
||||||
Output({'type': 'annotation', 'index': ALL}, 'value'),
|
|
||||||
Input('next-button', 'n_clicks'),
|
|
||||||
State('vis-com-name', 'data'),
|
|
||||||
State('image-container', 'src'),
|
|
||||||
State({'type': 'annotation', 'index': ALL}, 'id'),
|
|
||||||
State({'type': 'annotation', 'index': ALL}, 'value'),
|
|
||||||
prevent_initial_call=True,
|
|
||||||
)
|
|
||||||
def cycle_visual_communication_data(
|
|
||||||
n_clicks: int,
|
|
||||||
vis_com_name: str,
|
|
||||||
image_src: str,
|
|
||||||
annotation_keys: list,
|
|
||||||
annotation_values: list,
|
|
||||||
):
|
|
||||||
logging.info('began cycling visual communication data')
|
|
||||||
global collection
|
|
||||||
# prepare default response
|
|
||||||
response = [
|
|
||||||
'',
|
|
||||||
vis_com_name,
|
|
||||||
image_src,
|
|
||||||
annotation_values,
|
|
||||||
]
|
|
||||||
# check if next-button clicked
|
|
||||||
if n_clicks == 0:
|
|
||||||
logging.info('stopping early: next-button has not yet been clicked')
|
|
||||||
return response
|
|
||||||
# check if visual communication name is set
|
|
||||||
if len(vis_com_name) > 0:
|
|
||||||
logging.info('saving annotations to database: %s', vis_com_name)
|
|
||||||
try:
|
|
||||||
# extract option keys
|
|
||||||
annotation_keys = [
|
|
||||||
elem['index']
|
|
||||||
for elem in annotation_keys
|
|
||||||
]
|
|
||||||
# ensure all options are set
|
|
||||||
logging.info(annotation_keys)
|
|
||||||
for option, value in zip(annotation_keys, annotation_values):
|
|
||||||
if value is None:
|
|
||||||
raise ValueError(f"{option} is not set")
|
|
||||||
# prepare data to save
|
|
||||||
annotation_keys = [
|
|
||||||
elem.replace(' ', '_')
|
|
||||||
for elem
|
|
||||||
in annotation_keys
|
|
||||||
]
|
|
||||||
annotation_values = [
|
|
||||||
elem.replace(' ', '_').lower()
|
|
||||||
for elem
|
|
||||||
in annotation_values
|
|
||||||
]
|
|
||||||
annotation_map = {
|
|
||||||
key: value
|
|
||||||
for key, value
|
|
||||||
in zip(annotation_keys, annotation_values)
|
|
||||||
}
|
|
||||||
# instantiate ModelOutputs object
|
|
||||||
annotations = ModelData.from_annotations(**annotation_map)
|
|
||||||
# save data to database
|
|
||||||
upsert_annotation(
|
|
||||||
collection=collection,
|
|
||||||
vis_com_name=vis_com_name,
|
|
||||||
annotations=annotations,
|
|
||||||
)
|
|
||||||
except (ValueError, ValidationError) as exc:
|
|
||||||
msg = f"failed saving annotation: {exc}"
|
|
||||||
logging.warning(msg)
|
|
||||||
response[0] = msg
|
|
||||||
return tuple(response)
|
|
||||||
# get new visual communication
|
|
||||||
logging.info('trying to get new visual communication')
|
|
||||||
try:
|
|
||||||
# get data
|
|
||||||
vis_com = get_visual_communication(
|
|
||||||
collection=collection,
|
|
||||||
with_annotation=False,
|
|
||||||
)
|
|
||||||
# set variables
|
|
||||||
vis_com_name = vis_com.name
|
|
||||||
image_src = vis_com.webencoded_image()
|
|
||||||
if vis_com.prediction is not None:
|
|
||||||
# TODO: update to use optional predictions
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# reset annotations
|
|
||||||
annotation_values = [None for elem in annotation_values]
|
|
||||||
except NoDocumentFoundException:
|
|
||||||
msg = 'no unannotated data in database'
|
|
||||||
logging.warning(msg)
|
|
||||||
response[0] = msg
|
|
||||||
return tuple(response)
|
|
||||||
else:
|
|
||||||
response[1] = vis_com_name
|
|
||||||
response[2] = image_src
|
|
||||||
response[3] = annotation_values
|
|
||||||
logging.info('finished getting visual communication: %s', vis_com_name)
|
|
||||||
return tuple(response)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from .layout import app_layout
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import dash_mantine_components as dmc
|
|
||||||
from dash import html
|
|
||||||
from pathlib import Path
|
|
||||||
from base64 import b64encode
|
|
||||||
|
|
||||||
# read init img
|
|
||||||
init_img_path = Path(__file__).parent / "init_img.png"
|
|
||||||
with open(init_img_path.absolute(), "rb") as fh:
|
|
||||||
init_img_enc = b64encode(fh.read()).decode("utf-8")
|
|
||||||
# generate init img string
|
|
||||||
init_img_src = f"data:image/png;base64, {init_img_enc}"
|
|
||||||
|
|
||||||
image_element = dmc.Center(
|
|
||||||
html.Img(
|
|
||||||
style={
|
|
||||||
"width": "100%",
|
|
||||||
},
|
|
||||||
id="image-container",
|
|
||||||
src=init_img_src
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from core.database.classes import VisualCommunication
|
from shared.database.classes import VisualCommunication
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database import get_visual_communication
|
from shared.database import get_visual_communication
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# prepare env vars
|
# prepare env vars
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database.classes import VisualCommunication
|
from shared.database.classes import VisualCommunication
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# prepare env vars
|
# prepare env vars
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ from pathlib import Path
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from pymongo.errors import DuplicateKeyError
|
from pymongo.errors import DuplicateKeyError
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database.classes import VisualCommunication
|
from shared.database.classes import VisualCommunication
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# get list of image paths
|
# get list of image paths
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from pymongo.errors import DuplicateKeyError
|
from pymongo.errors import DuplicateKeyError
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database import VisualCommunication
|
from shared.database import VisualCommunication
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# get list of image paths
|
# get list of image paths
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from core.dto import ModelData
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -11,6 +11,9 @@ if __name__ == '__main__':
|
|||||||
in range(3)
|
in range(3)
|
||||||
]
|
]
|
||||||
# generate random predictions
|
# generate random predictions
|
||||||
[vis_com.generate_random_prediction() for vis_com in vis_com_list]
|
[
|
||||||
|
vis_com.from_random()
|
||||||
|
for vis_com in vis_com_list
|
||||||
|
]
|
||||||
for vis_com in vis_com_list:
|
for vis_com in vis_com_list:
|
||||||
print(vis_com)
|
print(vis_com)
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database import upsert_predictions
|
from shared.database import upsert_prediction
|
||||||
from core.database.classes import VisualCommunication
|
from shared.database.classes import VisualCommunication
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# setup logging
|
# setup logging
|
||||||
@@ -45,7 +45,7 @@ if __name__ == '__main__':
|
|||||||
for vis_com in vis_com_list:
|
for vis_com in vis_com_list:
|
||||||
if vis_com.prediction is None:
|
if vis_com.prediction is None:
|
||||||
continue
|
continue
|
||||||
upsert_predictions(
|
upsert_prediction(
|
||||||
collection=collection,
|
collection=collection,
|
||||||
vis_com_name=vis_com.name,
|
vis_com_name=vis_com.name,
|
||||||
predictions=vis_com.prediction,
|
predictions=vis_com.prediction,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database import total_documents
|
from shared.database import count_documents
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# prepare env vars
|
# prepare env vars
|
||||||
@@ -17,7 +17,8 @@ if __name__ == '__main__':
|
|||||||
# connect to database
|
# connect to database
|
||||||
collection, db, client = connect()
|
collection, db, client = connect()
|
||||||
# get visual communication
|
# get visual communication
|
||||||
num_docs = total_documents(
|
num_docs = count_documents(
|
||||||
collection=collection,
|
collection=collection,
|
||||||
|
only_with_annotation=True,
|
||||||
)
|
)
|
||||||
print(f"number of annotated documents in database: {num_docs}")
|
print(f"number of annotated documents in database: {num_docs}")
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from core.database import connect
|
from shared.database import connect
|
||||||
from core.database import total_documents
|
from shared.database import count_documents
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# prepare env vars
|
# prepare env vars
|
||||||
@@ -17,7 +17,8 @@ if __name__ == '__main__':
|
|||||||
# connect to database
|
# connect to database
|
||||||
collection, db, client = connect()
|
collection, db, client = connect()
|
||||||
# get visual communication
|
# get visual communication
|
||||||
num_docs = total_documents(
|
num_docs = count_documents(
|
||||||
collection=collection,
|
collection=collection,
|
||||||
|
only_with_annotation=False,
|
||||||
)
|
)
|
||||||
print(f"total number of documents in database: {num_docs}")
|
print(f"total number of documents in database: {num_docs}")
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
###########
|
# build stage
|
||||||
# BUILDER #
|
FROM python:3.12-slim-bookworm as BUILDER
|
||||||
###########
|
|
||||||
|
|
||||||
# pull official base image
|
|
||||||
FROM python:3.12-slim-bullseye as BUILDER
|
|
||||||
|
|
||||||
# set environment variables
|
# set environment variables
|
||||||
ENV PYTHONUNBUFFERED=1 \
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
@@ -33,15 +29,12 @@ ENV PATH="${POETRY_HOME}/bin:$PATH"
|
|||||||
|
|
||||||
# install runtime dependencies
|
# install runtime dependencies
|
||||||
WORKDIR ${APP_HOME}
|
WORKDIR ${APP_HOME}
|
||||||
COPY poetry.lock pyproject.toml ./
|
COPY ../poetry.lock ../pyproject.toml ./
|
||||||
RUN --mount=type=cache,target=${POETRY_CACHE_DIR} poetry install
|
RUN --mount=type=cache,target=${POETRY_CACHE_DIR} poetry install \
|
||||||
|
--with shared,web_ui
|
||||||
|
|
||||||
#########
|
# final stage
|
||||||
# FINAL #
|
FROM python:3.12-slim-bookworm
|
||||||
#########
|
|
||||||
|
|
||||||
# pull official base image
|
|
||||||
FROM python:3.12-slim-bullseye
|
|
||||||
|
|
||||||
# copy virtualenv made by poetry
|
# copy virtualenv made by poetry
|
||||||
ENV APP_HOME=/home/app \
|
ENV APP_HOME=/home/app \
|
||||||
@@ -56,8 +49,8 @@ RUN mkdir -p /home/app && \
|
|||||||
|
|
||||||
# add code while changing ownership
|
# add code while changing ownership
|
||||||
WORKDIR $APP_HOME
|
WORKDIR $APP_HOME
|
||||||
|
COPY --chown=app:app ../shared ./shared
|
||||||
COPY --chown=app:app ./src ./src
|
COPY --chown=app:app ./src ./src
|
||||||
COPY --chown=app:app ./core ./core
|
|
||||||
|
|
||||||
# change to the app user
|
# change to the app user
|
||||||
USER app
|
USER app
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
web_ui:
|
||||||
|
image: visual_critical_discourse_analysis:test
|
||||||
|
container_name: visual_critical_discourse_analysis_alone
|
||||||
|
build:
|
||||||
|
context: ../
|
||||||
|
dockerfile: Dockerfile.web_ui
|
||||||
|
ports:
|
||||||
|
- 8050:8050
|
||||||
|
env_file:
|
||||||
|
- ../server.env
|
||||||
|
environment:
|
||||||
|
- ENV=TEST
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .init_app import init_app
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
"""Definition of init_app function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import dash_bootstrap_components as dbc
|
||||||
|
from dash import ALL
|
||||||
|
from dash import Dash
|
||||||
|
from dash import Input
|
||||||
|
from dash import Output
|
||||||
|
from dash import State
|
||||||
|
from dash_auth import BasicAuth
|
||||||
|
from pydantic import ValidationError
|
||||||
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
|
from .layout import app_layout
|
||||||
|
from shared.database import count_documents
|
||||||
|
from shared.database import get_visual_communication
|
||||||
|
from shared.database import NoDocumentFoundException
|
||||||
|
from shared.database import upsert_annotation
|
||||||
|
from shared.database import upsert_visual_communication
|
||||||
|
from shared.database import VisualCommunication
|
||||||
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
|
def init_app(
|
||||||
|
collection: Collection,
|
||||||
|
) -> Dash:
|
||||||
|
"""Initialise web UI application."""
|
||||||
|
# setup app
|
||||||
|
app = Dash(
|
||||||
|
name='visual_critical_discourse_analysis_web_ui',
|
||||||
|
external_stylesheets=[
|
||||||
|
dbc.themes.BOOTSTRAP,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
app.title = 'visual critical discourse analysis'.title()
|
||||||
|
app.layout = app_layout
|
||||||
|
|
||||||
|
# setup authentication
|
||||||
|
auth_dict = {
|
||||||
|
os.getenv('DASH_AUTH_USERNAME'): os.getenv('DASH_AUTH_PASSWORD'),
|
||||||
|
}
|
||||||
|
BasicAuth(app, auth_dict)
|
||||||
|
|
||||||
|
# define callback: show warning
|
||||||
|
@app.callback(
|
||||||
|
Output('warning-element', 'is_open'),
|
||||||
|
Output('warning-element', 'children'),
|
||||||
|
Input('warning-message', 'data'),
|
||||||
|
)
|
||||||
|
def show_warning(
|
||||||
|
msg: str | None,
|
||||||
|
) -> tuple[bool, str]:
|
||||||
|
"""Show warning message in web ui."""
|
||||||
|
if msg is None or msg == '':
|
||||||
|
return False, ''
|
||||||
|
logging.debug('updated warning message: %s', msg)
|
||||||
|
return True, msg
|
||||||
|
|
||||||
|
# define callback: show info
|
||||||
|
@app.callback(
|
||||||
|
Output('info-element', 'is_open'),
|
||||||
|
Output('info-element', 'children'),
|
||||||
|
Input('info-message', 'data'),
|
||||||
|
)
|
||||||
|
def show_info(
|
||||||
|
msg: str | None,
|
||||||
|
) -> tuple[bool, str]:
|
||||||
|
"""Show info message in web ui."""
|
||||||
|
if msg is None or msg == '':
|
||||||
|
return False, ''
|
||||||
|
logging.debug('updated info message: %s', msg)
|
||||||
|
return True, msg
|
||||||
|
|
||||||
|
# define callback: update progress bar
|
||||||
|
@app.callback(
|
||||||
|
Output('progress-bar', 'value'),
|
||||||
|
Output('progress-bar', 'max'),
|
||||||
|
Output('progress-bar', 'label'),
|
||||||
|
Input('next-button', 'n_clicks'),
|
||||||
|
Input('upload-data', 'filename'),
|
||||||
|
)
|
||||||
|
def update_progress_bar(
|
||||||
|
n_clicks: int,
|
||||||
|
filename_list: list[str] | None,
|
||||||
|
) -> tuple[int, int, str]:
|
||||||
|
"""Update progress bar in web ui."""
|
||||||
|
assert isinstance(n_clicks, int)
|
||||||
|
if filename_list is not None:
|
||||||
|
assert isinstance(filename_list, list)
|
||||||
|
# get values from database
|
||||||
|
num_total = count_documents(
|
||||||
|
collection=collection,
|
||||||
|
only_with_annotation=False,
|
||||||
|
)
|
||||||
|
num_handled = count_documents(
|
||||||
|
collection=collection,
|
||||||
|
only_with_annotation=True,
|
||||||
|
)
|
||||||
|
limit = int(num_total/20)
|
||||||
|
label_str = f"{
|
||||||
|
num_handled
|
||||||
|
}/{num_total}" if num_handled >= limit else ''
|
||||||
|
return num_handled, num_total, label_str
|
||||||
|
|
||||||
|
# define callback: upload image
|
||||||
|
@app.callback(
|
||||||
|
Output('info-message', 'data', allow_duplicate=True),
|
||||||
|
Output('warning-message', 'data', allow_duplicate=True),
|
||||||
|
Input('upload-data', 'contents'),
|
||||||
|
State('upload-data', 'filename'),
|
||||||
|
prevent_initial_call=True,
|
||||||
|
)
|
||||||
|
def upload_images(
|
||||||
|
content_list: list[str] | None,
|
||||||
|
filename_list: list[str] | None,
|
||||||
|
) -> tuple[str | None, str | None]:
|
||||||
|
"""Upload image to database through web ui."""
|
||||||
|
# stop early if possible
|
||||||
|
if content_list is None or filename_list is None:
|
||||||
|
logging.info('nothing to upload.')
|
||||||
|
return None, 'nothing to upload'.title()
|
||||||
|
# build list of visual communication
|
||||||
|
vis_com_list = []
|
||||||
|
for content, filename in zip(content_list, filename_list):
|
||||||
|
try:
|
||||||
|
image = VisualCommunication.decode_image(content=content)
|
||||||
|
vis_com = VisualCommunication(
|
||||||
|
name=filename,
|
||||||
|
image=image,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.error('failed handling data from file: %s', filename)
|
||||||
|
logging.debug(exc)
|
||||||
|
continue
|
||||||
|
vis_com_list.append(vis_com)
|
||||||
|
# upsert documents
|
||||||
|
success = upsert_visual_communication(
|
||||||
|
collection=collection,
|
||||||
|
visual_communication_list=vis_com_list,
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
logging.info('uploaded %s files to database', len(vis_com_list))
|
||||||
|
return 'successfully uploaded images'.title(), None
|
||||||
|
logging.error('failed uploading images to database')
|
||||||
|
return None, 'failed uploading images'.title()
|
||||||
|
|
||||||
|
# define callback: cycle visual communication data
|
||||||
|
@app.callback(
|
||||||
|
Output('warning-message', 'data', allow_duplicate=True),
|
||||||
|
Output('vis-com-name', 'data'),
|
||||||
|
Output('image-container', 'src'),
|
||||||
|
Output({'type': 'annotation', 'index': ALL}, 'value'),
|
||||||
|
Input('next-button', 'n_clicks'),
|
||||||
|
State('vis-com-name', 'data'),
|
||||||
|
State('image-container', 'src'),
|
||||||
|
State({'type': 'annotation', 'index': ALL}, 'id'),
|
||||||
|
State({'type': 'annotation', 'index': ALL}, 'value'),
|
||||||
|
prevent_initial_call=True,
|
||||||
|
)
|
||||||
|
def cycle_vis_com_data(
|
||||||
|
n_clicks: int,
|
||||||
|
vis_com_name: str,
|
||||||
|
image_src: str,
|
||||||
|
annotation_keys: list,
|
||||||
|
annotation_values: list,
|
||||||
|
) -> tuple:
|
||||||
|
"""Cycle visual communcation data."""
|
||||||
|
assert isinstance(n_clicks, int)
|
||||||
|
assert isinstance(vis_com_name, str)
|
||||||
|
assert isinstance(image_src, str)
|
||||||
|
assert isinstance(annotation_keys, list)
|
||||||
|
assert isinstance(annotation_values, list)
|
||||||
|
# prepare default response
|
||||||
|
response = [
|
||||||
|
'',
|
||||||
|
vis_com_name,
|
||||||
|
image_src,
|
||||||
|
annotation_values,
|
||||||
|
]
|
||||||
|
# stop early if possible
|
||||||
|
if n_clicks == 0:
|
||||||
|
return tuple(response)
|
||||||
|
# check if visual communication name is set
|
||||||
|
if len(vis_com_name) > 0:
|
||||||
|
logging.info('saving annotations to database: %s', vis_com_name)
|
||||||
|
try:
|
||||||
|
# extract option keys
|
||||||
|
annotation_keys = [
|
||||||
|
elem['index']
|
||||||
|
for elem in annotation_keys
|
||||||
|
]
|
||||||
|
# ensure all options are set
|
||||||
|
logging.info(annotation_keys)
|
||||||
|
for option, value in zip(annotation_keys, annotation_values):
|
||||||
|
if value is None:
|
||||||
|
raise ValueError(f"{option} is not set")
|
||||||
|
# prepare data to save
|
||||||
|
annotation_keys = [
|
||||||
|
elem.replace(' ', '_')
|
||||||
|
for elem
|
||||||
|
in annotation_keys
|
||||||
|
]
|
||||||
|
annotation_values = [
|
||||||
|
elem.replace(' ', '_').lower()
|
||||||
|
for elem
|
||||||
|
in annotation_values
|
||||||
|
]
|
||||||
|
annotation_map = {
|
||||||
|
key: value
|
||||||
|
for key, value
|
||||||
|
in zip(annotation_keys, annotation_values)
|
||||||
|
}
|
||||||
|
# instantiate ModelOutputs object
|
||||||
|
annotations = ModelData.from_annotations(**annotation_map)
|
||||||
|
# save data to database
|
||||||
|
upsert_annotation(
|
||||||
|
collection=collection,
|
||||||
|
vis_com_name=vis_com_name,
|
||||||
|
annotations=annotations,
|
||||||
|
)
|
||||||
|
except (ValueError, ValidationError) as exc:
|
||||||
|
msg = f"failed saving annotation: {exc}"
|
||||||
|
logging.warning(msg)
|
||||||
|
response[0] = msg
|
||||||
|
return tuple(response)
|
||||||
|
# get new visual communication
|
||||||
|
logging.info('trying to get new visual communication')
|
||||||
|
try:
|
||||||
|
# get data
|
||||||
|
vis_com = get_visual_communication(
|
||||||
|
collection=collection,
|
||||||
|
with_annotation=False,
|
||||||
|
)
|
||||||
|
# set variables
|
||||||
|
vis_com_name = vis_com.name
|
||||||
|
image_src = vis_com.webencoded_image()
|
||||||
|
if vis_com.prediction is not None:
|
||||||
|
# TODO: update to use optional predictions
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# reset annotations
|
||||||
|
annotation_values = [None for elem in annotation_values]
|
||||||
|
except NoDocumentFoundException:
|
||||||
|
msg = 'no unannotated data in database'
|
||||||
|
logging.warning(msg)
|
||||||
|
response[0] = msg
|
||||||
|
return tuple(response)
|
||||||
|
else:
|
||||||
|
response[1] = vis_com_name
|
||||||
|
response[2] = image_src
|
||||||
|
response[3] = annotation_values
|
||||||
|
logging.info(
|
||||||
|
'finished getting visual communication: %s', vis_com_name,
|
||||||
|
)
|
||||||
|
return tuple(response)
|
||||||
|
logging.info('initialised app')
|
||||||
|
return app
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .layout import app_layout
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import dash_mantine_components as dmc
|
import dash_mantine_components as dmc
|
||||||
|
|
||||||
from .image import image_element
|
from .image import image_element
|
||||||
@@ -11,8 +13,8 @@ body_element = dmc.Container(
|
|||||||
grow=True,
|
grow=True,
|
||||||
children=[
|
children=[
|
||||||
dmc.Col([image_element], span=5),
|
dmc.Col([image_element], span=5),
|
||||||
dmc.Col([inputs_element], span=7)
|
dmc.Col([inputs_element], span=7),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from base64 import b64encode
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import dash_mantine_components as dmc
|
||||||
|
from dash import html
|
||||||
|
|
||||||
|
# read init img
|
||||||
|
init_img_path = Path(__file__).parent / 'init_img.png'
|
||||||
|
with open(init_img_path.absolute(), 'rb') as fh:
|
||||||
|
init_img_enc = b64encode(fh.read()).decode('utf-8')
|
||||||
|
# generate init img string
|
||||||
|
init_img_src = f"data:image/png;base64, {init_img_enc}"
|
||||||
|
|
||||||
|
image_element = dmc.Center(
|
||||||
|
html.Img(
|
||||||
|
style={
|
||||||
|
'width': '100%',
|
||||||
|
},
|
||||||
|
id='image-container',
|
||||||
|
src=init_img_src,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -1,23 +1,25 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import dash_mantine_components as dmc
|
import dash_mantine_components as dmc
|
||||||
|
|
||||||
from .labels import labels_element
|
from .labels import labels_element
|
||||||
|
|
||||||
next_button = dmc.Button(
|
next_button = dmc.Button(
|
||||||
"next".title(),
|
'next'.title(),
|
||||||
id="next-button",
|
id='next-button',
|
||||||
n_clicks=0,
|
n_clicks=0,
|
||||||
fullWidth=True,
|
fullWidth=True,
|
||||||
color="lime",
|
color='lime',
|
||||||
radius="sm",
|
radius='sm',
|
||||||
size="md",
|
size='md',
|
||||||
style={
|
style={
|
||||||
"height": "50px"
|
'height': '50px',
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
inputs_element = dmc.SimpleGrid(
|
inputs_element = dmc.SimpleGrid(
|
||||||
children=[
|
children=[
|
||||||
labels_element,
|
labels_element,
|
||||||
next_button
|
next_button,
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@@ -4,17 +4,17 @@ import dash_mantine_components as dmc
|
|||||||
from dash import dcc
|
from dash import dcc
|
||||||
from dash import html
|
from dash import html
|
||||||
|
|
||||||
from core.dto import AngleData
|
from shared.dto import AngleData
|
||||||
from core.dto import ContactData
|
from shared.dto import ContactData
|
||||||
from core.dto import DistanceData
|
from shared.dto import DistanceData
|
||||||
from core.dto import FramingData
|
from shared.dto import FramingData
|
||||||
from core.dto import InformationValueData
|
from shared.dto import InformationValueData
|
||||||
from core.dto import ModalityColorData
|
from shared.dto import ModalityColorData
|
||||||
from core.dto import ModalityDepthData
|
from shared.dto import ModalityDepthData
|
||||||
from core.dto import ModalityLightingData
|
from shared.dto import ModalityLightingData
|
||||||
from core.dto import PointOfViewData
|
from shared.dto import PointOfViewData
|
||||||
from core.dto import SalienceData
|
from shared.dto import SalienceData
|
||||||
from core.dto import VisualSyntaxData
|
from shared.dto import VisualSyntaxData
|
||||||
|
|
||||||
|
|
||||||
def generate_visual_syntax_options_map():
|
def generate_visual_syntax_options_map():
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import dash_mantine_components as dmc
|
import dash_mantine_components as dmc
|
||||||
|
|
||||||
from .stores import stores_element
|
|
||||||
from .alerts import alerts_element
|
from .alerts import alerts_element
|
||||||
from .header import header_element
|
|
||||||
from .body import body_element
|
from .body import body_element
|
||||||
|
from .header import header_element
|
||||||
|
from .stores import stores_element
|
||||||
|
|
||||||
|
|
||||||
app_layout = dmc.MantineProvider(
|
app_layout = dmc.MantineProvider(
|
||||||
theme={
|
theme={
|
||||||
"fontFamily": '"Inter", sans-serif',
|
'fontFamily': '"Inter", sans-serif',
|
||||||
"components": {
|
'components': {
|
||||||
"NavLink": {
|
'NavLink': {
|
||||||
"styles": {
|
'styles': {
|
||||||
"label": {
|
'label': {
|
||||||
"color": "#c2c7d0"
|
'color': '#c2c7d0',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
children=[
|
children=[
|
||||||
@@ -27,8 +29,8 @@ app_layout = dmc.MantineProvider(
|
|||||||
header_element,
|
header_element,
|
||||||
body_element,
|
body_element,
|
||||||
],
|
],
|
||||||
fluid=True
|
fluid=True,
|
||||||
),
|
),
|
||||||
|
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@@ -6,8 +6,8 @@ from dash import html
|
|||||||
|
|
||||||
stores_element = html.Div(
|
stores_element = html.Div(
|
||||||
children=[
|
children=[
|
||||||
dcc.Store(id='alert-message', data=''),
|
dcc.Store(id='warning-message', data=''),
|
||||||
dcc.Store(id='success-message', data=''),
|
dcc.Store(id='info-message', data=''),
|
||||||
dcc.Store(id='vis-com-name', data=''),
|
dcc.Store(id='vis-com-name', data=''),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
"""Definition of web_ui main script."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .app import init_app
|
||||||
|
from shared.database import connect
|
||||||
|
from shared.utils import check_env
|
||||||
|
from shared.utils import setup_logging
|
||||||
|
|
||||||
|
# ensure env vars set
|
||||||
|
check_env()
|
||||||
|
|
||||||
|
# setup logging stream handler
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
|
# connect to database
|
||||||
|
collection, db, client = connect()
|
||||||
|
|
||||||
|
# initialise application
|
||||||
|
app = init_app(collection=collection)
|
||||||
|
server = app.server
|
||||||
|
server.config.update(SECRET_KEY=os.urandom(24))
|
||||||
Reference in New Issue
Block a user