From 9fa3574a7d08bceffce144340783f148463cfcd0 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Mon, 11 Mar 2024 20:33:49 +0100 Subject: [PATCH 01/47] added dataloader, criterion, model definition and began defining training --- model/src/dataloader.py | 151 ++++++++++++++++++++++++++++++++++++++++ model/src/loss_fn.py | 7 ++ model/src/model.py | 37 ++++++++++ model/src/train.py | 6 ++ 4 files changed, 201 insertions(+) create mode 100644 model/src/dataloader.py create mode 100644 model/src/loss_fn.py create mode 100644 model/src/model.py create mode 100644 model/src/train.py diff --git a/model/src/dataloader.py b/model/src/dataloader.py new file mode 100644 index 0000000..d5cc0e5 --- /dev/null +++ b/model/src/dataloader.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import random +from io import BytesIO + +from torch import Tensor +from torch.utils.data import Dataset +from torchvision.io import read_image +from torchvision.transforms import ColorJitter +from torchvision.transforms import InterpolationMode +from torchvision.transforms import Normalize +from torchvision.transforms.functional import hflip +from torchvision.transforms.functional import pad +from torchvision.transforms.functional import resize +from torchvision.transforms.functional import rotate + +from core.database import connect +from core.database import list_names + +# resnet18 original normalization values +RESNET_NORMALIZE_MEAN = [0.485, 0.456, 0.406] +RESNET_NORMALIZE_STD = [0.229, 0.224, 0.225] + + +class VCDADataset(Dataset): + def __init__( + self, + do_augment: bool = False, + random_annotations: bool = False, + normalize_mean: list[float] = RESNET_NORMALIZE_MEAN, + normalize_std: list[float] = RESNET_NORMALIZE_STD, + ): + super().__init__() + self.do_augment = do_augment + self.random_annotations = random_annotations + self.normalize_mean = normalize_mean + self.normalize_std = normalize_std + # prepare augmentation functions + self.normalize = Normalize( + mean=normalize_mean, + std=normalize_std, + ) + self.color_jitter = ColorJitter( + brightness=1e-1, + contrast=8e-2, + saturation=8e-2, + ) + # connect to database + collection, _, _ = connect() + self.collection = collection + # load data names + has_annotation = False if self.random_annotations else True + self.data_name_list = list_names( + collection=self.collection, + has_annotation=has_annotation, + ) + + def __len__(self): + return len(self.data_name_list) + + def __getitem__(self, idx): + # get image from database + name = self.data_name_list[idx] + query = { + 'name': name, + } + projection = { + '_id': False, + 'image': True, + } + img_bytes = self.collection.find_one( + filter=query, + projection=projection, + ) + img = self.load_image(img_bytes) + if self.do_augment: + img = self.augment(img) + return img + + def load_image( + self, + data: bytes, + ) -> Tensor: + """Load images tensor from bytes.""" + assert isinstance(data, bytes) + img = read_image(BytesIO(data)) + img /= 255 # normalize 8-bit image + img = self.square_pad(img) + img = resize( + img=img, + size=(512, 512), + interpolation=InterpolationMode.BICUBIC, + ) + return img + + @staticmethod + def square_pad( + img: Tensor, + ) -> Tensor: + """ + Pads image to a square with side length + equal to the largest side of the input image. + """ + assert isinstance(img, Tensor) + # B, nc, w, h = img.shape + h = img.shape[-2] + w = img.shape[-1] + if h == w: + return img + max_wh = max([h, w]) + hp = int((max_wh - w) / 2) + vp = int((max_wh - h) / 2) + padding = (hp, vp, hp, vp) + return pad(img, padding, 0, 'constant') + + def augment( + self, + img: Tensor, + ) -> Tensor: + """ + Augment image with random horizontal flips, + rotations and color jitter. + """ + assert isinstance(img, Tensor) + # left-right flip + if random.random() >= 0.5: + img = hflip(img) + # rotation + rnd = random.random() + if rnd < 0.25: + img = rotate(img, angle=90) + if rnd < 0.5: + img = rotate(img, angle=180) + if rnd < 0.75: + img = rotate(img, angle=270) + # color jitter + img = self.color_jitter(img) + return img + + def reverse_normalise( + self, + img: Tensor, + ) -> Tensor: + """ + Reverse normalization to get an image + that can be interpreted by humans. + """ + assert isinstance(img, Tensor) + img *= Tensor(self.normalize_std).reshape((3, 1, 1)) + img += Tensor(self.normalize_mean).reshape((3, 1, 1)) + return img diff --git a/model/src/loss_fn.py b/model/src/loss_fn.py new file mode 100644 index 0000000..fe84786 --- /dev/null +++ b/model/src/loss_fn.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +import torch.nn as nn + + +def setup_criterion(): + return nn.MSELoss() diff --git a/model/src/model.py b/model/src/model.py new file mode 100644 index 0000000..e647fb5 --- /dev/null +++ b/model/src/model.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import torch.nn as nn +import torchvision + + +class ResNet18Head(nn.Module): + def __init__(self): + super().__init__() + # copy out parts from ResNet18 with weights + resnet18 = torchvision.models.resnet18( + weights=torchvision.models.ResNet18_Weights.IMAGENET1K_V1, + ) + # save relevant layers + self.conv1 = resnet18.conv1 + self.bn1 = resnet18.bn1 + self.relu = resnet18.relu + self.maxpool = resnet18.maxpool + self.layer1 = resnet18.layer1 + self.layer2 = resnet18.layer2 + self.layer3 = resnet18.layer3 + self.layer4 = resnet18.layer4 + self.avgpool = resnet18.avgpool + self.flat = nn.Flatten() # size 512 + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + x = self.avgpool(x) + x = self.flat(x) + return x diff --git a/model/src/train.py b/model/src/train.py new file mode 100644 index 0000000..c95e37f --- /dev/null +++ b/model/src/train.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from dataloader import VCDADataset # noqa: F401 +from loss_fn import setup_criterion # noqa: F401 + +from model import ResNet18Head # noqa: F401 From ded50d809577589e572b4f924cc4bb4f64cc29f1 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Mon, 11 Mar 2024 20:36:14 +0100 Subject: [PATCH 02/47] moved shared functions --- {src => core}/database/__init__.py | 0 {src => core}/database/classes.py | 0 {src => core}/database/database.py | 23 +++++++++++++---------- {src => core}/database/utils.py | 0 4 files changed, 13 insertions(+), 10 deletions(-) rename {src => core}/database/__init__.py (100%) rename {src => core}/database/classes.py (100%) rename {src => core}/database/database.py (51%) rename {src => core}/database/utils.py (100%) diff --git a/src/database/__init__.py b/core/database/__init__.py similarity index 100% rename from src/database/__init__.py rename to core/database/__init__.py diff --git a/src/database/classes.py b/core/database/classes.py similarity index 100% rename from src/database/classes.py rename to core/database/classes.py diff --git a/src/database/database.py b/core/database/database.py similarity index 51% rename from src/database/database.py rename to core/database/database.py index 8f10ac8..3d73964 100644 --- a/src/database/database.py +++ b/core/database/database.py @@ -1,24 +1,27 @@ -from pymongo import MongoClient -from dotenv import load_dotenv +from __future__ import annotations + import logging import os +from dotenv import load_dotenv +from pymongo import MongoClient + def connect(): """Connect to MongoDB.""" # load env vars load_dotenv() necessary_env_vars = [ - "MONGO_HOST", - "MONGO_DB", - "MONGO_COLLECTION" + 'MONGO_HOST', + 'MONGO_DB', + 'MONGO_COLLECTION', ] for env_var in necessary_env_vars: assert env_var in os.environ, f"{env_var} not found" # connect to database - client = MongoClient(os.getenv("MONGO_HOST")) - db = client[os.getenv("MONGO_DB")] - collection = db[os.getenv("MONGO_COLLECTION")] - collection.create_index("name", unique=True) - logging.info("connected to database") + client = MongoClient(os.getenv('MONGO_HOST')) + db = client[os.getenv('MONGO_DB')] + collection = db[os.getenv('MONGO_COLLECTION')] + collection.create_index('name', unique=True) + logging.info('connected to database') return collection, db, client diff --git a/src/database/utils.py b/core/database/utils.py similarity index 100% rename from src/database/utils.py rename to core/database/utils.py From 1e339cecf4da9c0dbafc21b536d97cfa7b88f704 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Mon, 11 Mar 2024 20:38:48 +0100 Subject: [PATCH 03/47] added database util functions --- core/database/__init__.py | 1 + core/database/utils.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/core/database/__init__.py b/core/database/__init__.py index 0990ebc..e964218 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -5,6 +5,7 @@ from .classes import NoDocumentFoundException from .classes import VisualCommunication from .database import connect from .utils import get_visual_communication +from .utils import list_names from .utils import total_annotated from .utils import total_documents from .utils import upsert_annotations diff --git a/core/database/utils.py b/core/database/utils.py index d43fa77..581b807 100644 --- a/core/database/utils.py +++ b/core/database/utils.py @@ -9,6 +9,44 @@ from .classes import NoDocumentFoundException from .classes import VisualCommunication +def count_documents( + collection: Collection, + has_annotation: bool = False, +) -> int: + """ + Get the total number of documents + in database that matches the filters. + """ + assert isinstance(collection, Collection) + assert isinstance(has_annotation, bool) + # build query + query = {} + if has_annotation: + query['annotation'] = {'$ne': None} + return collection.count_documents(filter=query) + + +def list_names( + collection: Collection, + has_annotation: bool = True, +) -> list[str]: + """List the names of entries that match the filters.""" + assert isinstance(collection, Collection) + assert isinstance(has_annotation, bool) + # build query + query = {} + if has_annotation: + query['annotation'] = {'$ne': None} + res_list = collection.find( + filter=query, + projection={ + '_id': False, + 'name': True, + }, + ) + return list(res_list) + + def total_documents( collection: Collection, ) -> int: From 93fb18fde2483e24c8f0b8513aa7a350a97b9a87 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Mon, 11 Mar 2024 20:39:20 +0100 Subject: [PATCH 04/47] installed packages --- poetry.lock | 344 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 5 + 2 files changed, 347 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6dc5a65..230ad5c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -381,6 +381,22 @@ idna = ["idna (>=3.6)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] +[[package]] +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + [[package]] name = "flake8" version = "7.0.0" @@ -419,6 +435,41 @@ Werkzeug = ">=3.0.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "fsspec" +version = "2024.2.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.2.0-py3-none-any.whl", hash = "sha256:817f969556fa5916bc682e02ca2045f96ff7f586d45110fcb76022063ad2c7d8"}, + {file = "fsspec-2024.2.0.tar.gz", hash = "sha256:b6ad1a679f760dda52b1168c859d01b7b80648ea6f7f7c7f5a8a91dc3f3ecb84"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +devel = ["pytest", "pytest-cov"] +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"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +tqdm = ["tqdm"] + [[package]] name = "gunicorn" version = "21.2.0" @@ -588,6 +639,23 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "mypy" version = "1.8.0" @@ -656,6 +724,24 @@ files = [ {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] +[[package]] +name = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + [[package]] name = "numpy" version = "1.26.4" @@ -701,6 +787,147 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.1.3.1" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, + {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.1.105" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, + {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.1.105" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, + {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.1.105" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, + {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "8.9.2.26" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl", hash = "sha256:5ccb288774fdfb07a7e7025ffec286971c06d8d7b4fb162525334616d7629ff9"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.0.2.54" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, + {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.2.106" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, + {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.4.5.107" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, + {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.1.0.106" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, + {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.19.3" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nccl_cu12-2.19.3-py3-none-manylinux1_x86_64.whl", hash = "sha256:a9734707a2c96443331c1e48c717024aa6678a0e2a4cb66b2c364d18cee6b48d"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.99" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c6428836d20fe7e327191c175791d38570e10762edc588fb46749217cd444c74"}, + {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-win_amd64.whl", hash = "sha256:991905ffa2144cb603d8ca7962d75c35334ae82bf92820b6ba78157277da1ad2"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.1.105" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +files = [ + {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, + {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, +] + [[package]] name = "outcome" version = "1.3.0.post0" @@ -1320,6 +1547,20 @@ files = [ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] +[[package]] +name = "sympy" +version = "1.12" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, + {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, +] + +[package.dependencies] +mpmath = ">=0.19" + [[package]] name = "tenacity" version = "8.2.3" @@ -1334,6 +1575,105 @@ files = [ [package.extras] doc = ["reno", "sphinx", "tornado (>=4.5)"] +[[package]] +name = "torch" +version = "2.2.1" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "torch-2.2.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:8d3bad336dd2c93c6bcb3268e8e9876185bda50ebde325ef211fb565c7d15273"}, + {file = "torch-2.2.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:5297f13370fdaca05959134b26a06a7f232ae254bf2e11a50eddec62525c9006"}, + {file = "torch-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:5f5dee8433798888ca1415055f5e3faf28a3bad660e4c29e1014acd3275ab11a"}, + {file = "torch-2.2.1-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:b6d78338acabf1fb2e88bf4559d837d30230cf9c3e4337261f4d83200df1fcbe"}, + {file = "torch-2.2.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:6ab3ea2e29d1aac962e905142bbe50943758f55292f1b4fdfb6f4792aae3323e"}, + {file = "torch-2.2.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:d86664ec85902967d902e78272e97d1aff1d331f7619d398d3ffab1c9b8e9157"}, + {file = "torch-2.2.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d6227060f268894f92c61af0a44c0d8212e19cb98d05c20141c73312d923bc0a"}, + {file = "torch-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:77e990af75fb1675490deb374d36e726f84732cd5677d16f19124934b2409ce9"}, + {file = "torch-2.2.1-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:46085e328d9b738c261f470231e987930f4cc9472d9ffb7087c7a1343826ac51"}, + {file = "torch-2.2.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:2d9e7e5ecbb002257cf98fae13003abbd620196c35f85c9e34c2adfb961321ec"}, + {file = "torch-2.2.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:ada53aebede1c89570e56861b08d12ba4518a1f8b82d467c32665ec4d1f4b3c8"}, + {file = "torch-2.2.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:be21d4c41ecebed9e99430dac87de1439a8c7882faf23bba7fea3fea7b906ac1"}, + {file = "torch-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:79848f46196750367dcdf1d2132b722180b9d889571e14d579ae82d2f50596c5"}, + {file = "torch-2.2.1-cp312-none-macosx_10_9_x86_64.whl", hash = "sha256:7ee804847be6be0032fbd2d1e6742fea2814c92bebccb177f0d3b8e92b2d2b18"}, + {file = "torch-2.2.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:84b2fb322ab091039fdfe74e17442ff046b258eb5e513a28093152c5b07325a7"}, + {file = "torch-2.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5c0c83aa7d94569997f1f474595e808072d80b04d34912ce6f1a0e1c24b0c12a"}, + {file = "torch-2.2.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:91a1b598055ba06b2c386415d2e7f6ac818545e94c5def597a74754940188513"}, + {file = "torch-2.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f93ddf3001ecec16568390b507652644a3a103baa72de3ad3b9c530e3277098"}, + {file = "torch-2.2.1-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:0e8bdd4c77ac2584f33ee14c6cd3b12767b4da508ec4eed109520be7212d1069"}, + {file = "torch-2.2.1-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:6a21bcd7076677c97ca7db7506d683e4e9db137e8420eb4a68fb67c3668232a7"}, + {file = "torch-2.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f1b90ac61f862634039265cd0f746cc9879feee03ff962c803486301b778714b"}, + {file = "torch-2.2.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:ed9e29eb94cd493b36bca9cb0b1fd7f06a0688215ad1e4b3ab4931726e0ec092"}, + {file = "torch-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:c47bc25744c743f3835831a20efdcfd60aeb7c3f9804a213f61e45803d16c2a5"}, + {file = "torch-2.2.1-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:0952549bcb43448c8d860d5e3e947dd18cbab491b14638e21750cb3090d5ad3e"}, + {file = "torch-2.2.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:26bd2272ec46fc62dcf7d24b2fb284d44fcb7be9d529ebf336b9860350d674ed"}, +] + +[package.dependencies] +filelock = "*" +fsspec = "*" +jinja2 = "*" +networkx = "*" +nvidia-cublas-cu12 = {version = "12.1.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "8.9.2.26", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.0.2.54", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.2.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.4.5.107", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.1.0.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.19.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +sympy = "*" +typing-extensions = ">=4.8.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.9.1)"] + +[[package]] +name = "torchvision" +version = "0.17.1" +description = "image and video datasets and models for torch deep learning" +optional = false +python-versions = ">=3.8" +files = [ + {file = "torchvision-0.17.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:06418880212b66e45e855dd39f536e7fd48b4e6b034a11dd9fe9e2384afb51ec"}, + {file = "torchvision-0.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33d65d0c7fdcb3f7bc1dd8ed30ea3cd7e0587b4ad1b104b5677c8191a8bad9f1"}, + {file = "torchvision-0.17.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:aaefef2be6a02f206085ce4bb6c0078b03ebf48cb6ff82bd762ff6248475e08e"}, + {file = "torchvision-0.17.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ebe5fdb466aff8a8e8e755de84a843418b6f8d500624752c05eaa638d7700f3d"}, + {file = "torchvision-0.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:9d4d45a996f4313e9c5db4da71d31508d44f7ccfbf29d3442bdcc2ad13e0b6f3"}, + {file = "torchvision-0.17.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:ea2ccdbf5974e0bf27fd6644a33b19cb0700297cf397bb0469e762c11c6c4105"}, + {file = "torchvision-0.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9106e32c9f1e70afa8172cf1b064cf9c2998d8dff0769ec69d537b20209ee43d"}, + {file = "torchvision-0.17.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:5966936c669a08870f6547cd0a90d08b157aeda03293f79e2adbb934687175ed"}, + {file = "torchvision-0.17.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e74f5a26ef8190eab0c38b3f63914fea94e58e3b2f0e5466611c9f63bd91a80b"}, + {file = "torchvision-0.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:a2109c1a1dcf71e8940d43e91f78c4dd5bf0fcefb3a0a42244102752009f5862"}, + {file = "torchvision-0.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5d241d2a5fb4e608677fccf6f80b34a124446d324ee40c7814ce54bce888275b"}, + {file = "torchvision-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e0fe98d9d92c23d2262ff82f973242951b9357fb640f8888ac50848bd00f5b45"}, + {file = "torchvision-0.17.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:32dc5de86d2ade399e11087095674ca08a1649fb322cfe69336d28add467edcb"}, + {file = "torchvision-0.17.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:54902877410ffb5458ee52b6d0de4b25cf01496bee736d6825301a5f0398536e"}, + {file = "torchvision-0.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:cc22c1ed0f1aba3f98fd72b6f60021f57aec1d2f6af518522e8a0a83848de3a8"}, + {file = "torchvision-0.17.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:2621097065fa1c827885e2b52102e839a3541b933b7a90e0fa3c42c3de1bc3cf"}, + {file = "torchvision-0.17.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5ce76466af2b5a30573939cae1e6e62e29316ceb3ee748091002f312ab0912f6"}, + {file = "torchvision-0.17.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bd5dcd14a32945c72f5c19341add94aa7c23dd7bca2bafde44d0f3c4344d17ed"}, + {file = "torchvision-0.17.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dca22795cc02ca0d5ddc08c1422ff620bc9899f63d15dc36f71ef37250e17b75"}, + {file = "torchvision-0.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:524405457dd97d9ab0e48df502f819d0f41a113ce8f00470bb9926d9d36efcf1"}, + {file = "torchvision-0.17.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:58299a724b37b893c7ce4d0b32ea1480c30e467cc114167964b45f6013f6c2d3"}, + {file = "torchvision-0.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a1b17fb158b2b881f2c8796fe1839a624e49d5fd07aa61f6dae60ba4819421a"}, + {file = "torchvision-0.17.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:429d63eb7551aa4d8f6cdf08d109b5570c20cbcce36d9cb95b24556418e4dc82"}, + {file = "torchvision-0.17.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0ecc9a58171bd555aed583bf2f72e7fd6cc4f767c14f8b80b6a8725eacf4ceb1"}, + {file = "torchvision-0.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:5f427ebee15521edcd836bfe05e86feb5189b5c943b9e3999ed0e3f391fbaa1d"}, +] + +[package.dependencies] +numpy = "*" +pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" +torch = "2.2.1" + +[package.extras] +scipy = ["scipy"] + [[package]] name = "trio" version = "0.24.0" @@ -1486,4 +1826,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "849178e665e9a43935d7508528a82d8e0e173020b9ee4256e6a25d3e607f1bd8" +content-hash = "6c4bb66c774e93a08e4180f3148942edf91e2b5620ea1f6c3e4c30045bb643e5" diff --git a/pyproject.toml b/pyproject.toml index 4d330e0..95dfad4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,11 @@ selenium = "^4.18.1" webdriver-manager = "^4.0.1" retry = "^0.9.2" + +[tool.poetry.group.model.dependencies] +torch = "^2.2.1" +torchvision = "^0.17.1" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From 1065a35cf0034aa4e5b73cafd2ee858d853c6a0d Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 18:42:42 +0100 Subject: [PATCH 05/47] removed unused file --- model/src/loss_fn.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 model/src/loss_fn.py diff --git a/model/src/loss_fn.py b/model/src/loss_fn.py deleted file mode 100644 index fe84786..0000000 --- a/model/src/loss_fn.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -import torch.nn as nn - - -def setup_criterion(): - return nn.MSELoss() From ec48879327dad22a6adcdac07bd470831e2cb170 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 18:45:35 +0100 Subject: [PATCH 06/47] updated to use data_name_list --- model/src/dataloader.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/model/src/dataloader.py b/model/src/dataloader.py index d5cc0e5..77c433e 100644 --- a/model/src/dataloader.py +++ b/model/src/dataloader.py @@ -15,7 +15,6 @@ from torchvision.transforms.functional import resize from torchvision.transforms.functional import rotate from core.database import connect -from core.database import list_names # resnet18 original normalization values RESNET_NORMALIZE_MEAN = [0.485, 0.456, 0.406] @@ -25,12 +24,14 @@ RESNET_NORMALIZE_STD = [0.229, 0.224, 0.225] class VCDADataset(Dataset): def __init__( self, + data_name_list: list[str], do_augment: bool = False, random_annotations: bool = False, normalize_mean: list[float] = RESNET_NORMALIZE_MEAN, normalize_std: list[float] = RESNET_NORMALIZE_STD, ): super().__init__() + self.data_name_list = data_name_list self.do_augment = do_augment self.random_annotations = random_annotations self.normalize_mean = normalize_mean @@ -48,12 +49,6 @@ class VCDADataset(Dataset): # connect to database collection, _, _ = connect() self.collection = collection - # load data names - has_annotation = False if self.random_annotations else True - self.data_name_list = list_names( - collection=self.collection, - has_annotation=has_annotation, - ) def __len__(self): return len(self.data_name_list) From f1392067586390d286e6104025b54f827adf957c Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 21:50:47 +0100 Subject: [PATCH 07/47] added model definitions --- model/src/models/__init__.py | 0 model/src/models/angle.py | 8 ++++++ model/src/models/contact.py | 8 ++++++ model/src/models/distance.py | 8 ++++++ model/src/models/framing.py | 8 ++++++ model/src/models/fully_connected.py | 26 +++++++++++++++++++ model/src/models/information_value.py | 8 ++++++ model/src/models/modality_color.py | 8 ++++++ model/src/models/modality_depth.py | 8 ++++++ model/src/models/modality_lighting.py | 8 ++++++ model/src/models/point_of_view.py | 8 ++++++ .../src/{model.py => models/resnet18_head.py} | 0 model/src/models/salience.py | 8 ++++++ model/src/models/visual_syntax.py | 8 ++++++ 14 files changed, 114 insertions(+) create mode 100644 model/src/models/__init__.py create mode 100644 model/src/models/angle.py create mode 100644 model/src/models/contact.py create mode 100644 model/src/models/distance.py create mode 100644 model/src/models/framing.py create mode 100644 model/src/models/fully_connected.py create mode 100644 model/src/models/information_value.py create mode 100644 model/src/models/modality_color.py create mode 100644 model/src/models/modality_depth.py create mode 100644 model/src/models/modality_lighting.py create mode 100644 model/src/models/point_of_view.py rename model/src/{model.py => models/resnet18_head.py} (100%) create mode 100644 model/src/models/salience.py create mode 100644 model/src/models/visual_syntax.py diff --git a/model/src/models/__init__.py b/model/src/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/model/src/models/angle.py b/model/src/models/angle.py new file mode 100644 index 0000000..2fde0ad --- /dev/null +++ b/model/src/models/angle.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class AngleTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/contact.py b/model/src/models/contact.py new file mode 100644 index 0000000..be69b23 --- /dev/null +++ b/model/src/models/contact.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class ContactTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=2) diff --git a/model/src/models/distance.py b/model/src/models/distance.py new file mode 100644 index 0000000..d5a7d8c --- /dev/null +++ b/model/src/models/distance.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class DistanceTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/framing.py b/model/src/models/framing.py new file mode 100644 index 0000000..25a6c10 --- /dev/null +++ b/model/src/models/framing.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class FramingTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=4) diff --git a/model/src/models/fully_connected.py b/model/src/models/fully_connected.py new file mode 100644 index 0000000..a5bb915 --- /dev/null +++ b/model/src/models/fully_connected.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import torch.nn as nn + + +class FullyConnectedModel(nn.Module): + def __init__(self, num_out_features: int): + super().__init__() + # define layers + self.fc1 = nn.Linear(in_features=16*16*512, out_features=512) + self.af1 = nn.ReLU() + self.fc2 = nn.Linear(in_features=512, out_features=128) + self.af2 = nn.ReLU() + self.fc3 = nn.Linear(in_features=128, out_features=32) + self.af3 = nn.ReLU() + self.fc4 = nn.Linear(in_features=32, out_features=num_out_features) + + def forward(self, x): + x = self.fc1(x) + x = self.af1(x) + x = self.fc2(x) + x = self.af2(x) + x = self.fc3(x) + x = self.af3(x) + x = self.fc4(x) + return x diff --git a/model/src/models/information_value.py b/model/src/models/information_value.py new file mode 100644 index 0000000..31c2848 --- /dev/null +++ b/model/src/models/information_value.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class InformationValueTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/modality_color.py b/model/src/models/modality_color.py new file mode 100644 index 0000000..ae10bd8 --- /dev/null +++ b/model/src/models/modality_color.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class ModalityColorTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/modality_depth.py b/model/src/models/modality_depth.py new file mode 100644 index 0000000..3c0e5e8 --- /dev/null +++ b/model/src/models/modality_depth.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class ModalityDepthTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/modality_lighting.py b/model/src/models/modality_lighting.py new file mode 100644 index 0000000..8913281 --- /dev/null +++ b/model/src/models/modality_lighting.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class ModalityLightingTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=3) diff --git a/model/src/models/point_of_view.py b/model/src/models/point_of_view.py new file mode 100644 index 0000000..d0d8ae3 --- /dev/null +++ b/model/src/models/point_of_view.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class PointOfViewTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=2) diff --git a/model/src/model.py b/model/src/models/resnet18_head.py similarity index 100% rename from model/src/model.py rename to model/src/models/resnet18_head.py diff --git a/model/src/models/salience.py b/model/src/models/salience.py new file mode 100644 index 0000000..bc9f0af --- /dev/null +++ b/model/src/models/salience.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class SalienceTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=5) diff --git a/model/src/models/visual_syntax.py b/model/src/models/visual_syntax.py new file mode 100644 index 0000000..fe02a2b --- /dev/null +++ b/model/src/models/visual_syntax.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .fully_connected import FullyConnectedModel + + +class VisualSyntaxTail(FullyConnectedModel): + def __init__(self): + super().__init__(num_out_features=18) From 08c5c14da67ca75eb1c7349394a90e26fbd4732c Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 22:38:53 +0100 Subject: [PATCH 08/47] added definitions of data models --- core/data_models/__init__.py | 13 ++++++ core/data_models/angle.py | 9 ++++ core/data_models/contact.py | 8 ++++ core/data_models/data_model.py | 59 +++++++++++++++++++++++++++ core/data_models/distance.py | 9 ++++ core/data_models/framing.py | 10 +++++ core/data_models/information_value.py | 9 ++++ core/data_models/modality_color.py | 9 ++++ core/data_models/modality_depth.py | 9 ++++ core/data_models/modality_lighting.py | 9 ++++ core/data_models/point_of_view.py | 8 ++++ core/data_models/salience.py | 11 +++++ core/data_models/visual_syntax.py | 24 +++++++++++ 13 files changed, 187 insertions(+) create mode 100644 core/data_models/__init__.py create mode 100644 core/data_models/angle.py create mode 100644 core/data_models/contact.py create mode 100644 core/data_models/data_model.py create mode 100644 core/data_models/distance.py create mode 100644 core/data_models/framing.py create mode 100644 core/data_models/information_value.py create mode 100644 core/data_models/modality_color.py create mode 100644 core/data_models/modality_depth.py create mode 100644 core/data_models/modality_lighting.py create mode 100644 core/data_models/point_of_view.py create mode 100644 core/data_models/salience.py create mode 100644 core/data_models/visual_syntax.py diff --git a/core/data_models/__init__.py b/core/data_models/__init__.py new file mode 100644 index 0000000..a0b2400 --- /dev/null +++ b/core/data_models/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .angle import AngleData +from .contact import ContactData +from .distance import DistanceData +from .framing import FramingData +from .information_value import InformationValueData +from .modality_color import ModalityColorData +from .modality_depth import ModalityDepthData +from .modality_lighting import ModalityLightingData +from .point_of_view import PointOfViewData +from .salience import SalienceData +from .visual_syntax import VisualSyntaxData diff --git a/core/data_models/angle.py b/core/data_models/angle.py new file mode 100644 index 0000000..4af953c --- /dev/null +++ b/core/data_models/angle.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class AngleData(DataModel): + high: float + eye_level: float + low: float diff --git a/core/data_models/contact.py b/core/data_models/contact.py new file mode 100644 index 0000000..79f8909 --- /dev/null +++ b/core/data_models/contact.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class ContactData(DataModel): + offer: float + demand: float diff --git a/core/data_models/data_model.py b/core/data_models/data_model.py new file mode 100644 index 0000000..d0b4667 --- /dev/null +++ b/core/data_models/data_model.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import random + +from pydantic import BaseModel +from pydantic import ValidationError + + +class DataModel(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()) diff --git a/core/data_models/distance.py b/core/data_models/distance.py new file mode 100644 index 0000000..29236c6 --- /dev/null +++ b/core/data_models/distance.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class DistanceData(DataModel): + long: float + medium: float + close: float diff --git a/core/data_models/framing.py b/core/data_models/framing.py new file mode 100644 index 0000000..0a572e8 --- /dev/null +++ b/core/data_models/framing.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class FramingData(DataModel): + frame_lines: float + empty_space: float + colour_contrast: float + form_contrast: float diff --git a/core/data_models/information_value.py b/core/data_models/information_value.py new file mode 100644 index 0000000..0e8ef23 --- /dev/null +++ b/core/data_models/information_value.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class InformationValueData(DataModel): + given_new: float + ideal_real: float + central_marginal: float diff --git a/core/data_models/modality_color.py b/core/data_models/modality_color.py new file mode 100644 index 0000000..cad5b1a --- /dev/null +++ b/core/data_models/modality_color.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityColorData(DataModel): + high: float + medium: float + low: float diff --git a/core/data_models/modality_depth.py b/core/data_models/modality_depth.py new file mode 100644 index 0000000..04dda66 --- /dev/null +++ b/core/data_models/modality_depth.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityDepthData(DataModel): + high: float + medium: float + low: float diff --git a/core/data_models/modality_lighting.py b/core/data_models/modality_lighting.py new file mode 100644 index 0000000..1ff31ec --- /dev/null +++ b/core/data_models/modality_lighting.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityLightingData(DataModel): + high: float + medium: float + low: float diff --git a/core/data_models/point_of_view.py b/core/data_models/point_of_view.py new file mode 100644 index 0000000..12a2cbe --- /dev/null +++ b/core/data_models/point_of_view.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class PointOfViewData(DataModel): + frontal: float + oblique: float diff --git a/core/data_models/salience.py b/core/data_models/salience.py new file mode 100644 index 0000000..40b73eb --- /dev/null +++ b/core/data_models/salience.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class SalienceData(DataModel): + size: float + colour: float + tone: float + form: float + positioning: float diff --git a/core/data_models/visual_syntax.py b/core/data_models/visual_syntax.py new file mode 100644 index 0000000..760638f --- /dev/null +++ b/core/data_models/visual_syntax.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from .data_model import DataModel + + +class VisualSyntaxData(DataModel): + 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 From 865a8ff2b70247f4892f5470edfed554cb0027ca Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 22:44:10 +0100 Subject: [PATCH 09/47] added instantiation from list --- core/data_models/data_model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/data_models/data_model.py b/core/data_models/data_model.py index d0b4667..9650328 100644 --- a/core/data_models/data_model.py +++ b/core/data_models/data_model.py @@ -37,6 +37,12 @@ class DataModel(BaseModel): kwargs[option] = 1 return cls(**kwargs) + @classmethod + def from_list(cls, data_list: list[float]): + """Instantiate from list of values.""" + kwargs = {key: val for key, val in zip(cls.list_fields(), data_list)} + return cls(**kwargs) + def __repr__(self) -> str: model_dict = self.model_dump() model_repr_str = f"{self.classname()}(" From b9f333c7d0a1a4b5515491976f27428ade6347e9 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 12 Mar 2024 22:54:50 +0100 Subject: [PATCH 10/47] began defining visual communication model --- model/src/models/visual_communication.py | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 model/src/models/visual_communication.py diff --git a/model/src/models/visual_communication.py b/model/src/models/visual_communication.py new file mode 100644 index 0000000..e11a81f --- /dev/null +++ b/model/src/models/visual_communication.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +import torch.nn as nn + +from .resnet18_head import ResNet18Head +from .visual_syntax import VisualSyntaxTail +from core.data_models import VisualSyntaxData + + +class VisualCommunicationModel(nn.Module): + def __init__(self): + super().__init__() + # store other models + self.resnet_head = ResNet18Head() + self.visual_syntax_tail = VisualSyntaxTail() + + def forward(self, x): + # generate visual representation + vis_rep = self.resnet_head(x) + # prepare result map + results = {} + # predict visual syntax + visual_syntax_pred = self.visual_syntax_tail(vis_rep).cpu() + # visual_syntax_pred = float() + results['visual_syntax'] = VisualSyntaxData.from_list( + visual_syntax_pred, + ) + + return results From 54eca39b6d6be25e3019772e605402554e8b4b90 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:27:49 +0100 Subject: [PATCH 11/47] renamed dir --- core/{data_models => dto}/__init__.py | 0 core/{data_models => dto}/angle.py | 0 core/{data_models => dto}/contact.py | 0 core/{data_models => dto}/data_model.py | 0 core/{data_models => dto}/distance.py | 0 core/{data_models => dto}/framing.py | 0 core/{data_models => dto}/information_value.py | 0 core/{data_models => dto}/modality_color.py | 0 core/{data_models => dto}/modality_depth.py | 0 core/{data_models => dto}/modality_lighting.py | 0 core/{data_models => dto}/point_of_view.py | 0 core/{data_models => dto}/salience.py | 0 core/{data_models => dto}/visual_syntax.py | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename core/{data_models => dto}/__init__.py (100%) rename core/{data_models => dto}/angle.py (100%) rename core/{data_models => dto}/contact.py (100%) rename core/{data_models => dto}/data_model.py (100%) rename core/{data_models => dto}/distance.py (100%) rename core/{data_models => dto}/framing.py (100%) rename core/{data_models => dto}/information_value.py (100%) rename core/{data_models => dto}/modality_color.py (100%) rename core/{data_models => dto}/modality_depth.py (100%) rename core/{data_models => dto}/modality_lighting.py (100%) rename core/{data_models => dto}/point_of_view.py (100%) rename core/{data_models => dto}/salience.py (100%) rename core/{data_models => dto}/visual_syntax.py (100%) diff --git a/core/data_models/__init__.py b/core/dto/__init__.py similarity index 100% rename from core/data_models/__init__.py rename to core/dto/__init__.py diff --git a/core/data_models/angle.py b/core/dto/angle.py similarity index 100% rename from core/data_models/angle.py rename to core/dto/angle.py diff --git a/core/data_models/contact.py b/core/dto/contact.py similarity index 100% rename from core/data_models/contact.py rename to core/dto/contact.py diff --git a/core/data_models/data_model.py b/core/dto/data_model.py similarity index 100% rename from core/data_models/data_model.py rename to core/dto/data_model.py diff --git a/core/data_models/distance.py b/core/dto/distance.py similarity index 100% rename from core/data_models/distance.py rename to core/dto/distance.py diff --git a/core/data_models/framing.py b/core/dto/framing.py similarity index 100% rename from core/data_models/framing.py rename to core/dto/framing.py diff --git a/core/data_models/information_value.py b/core/dto/information_value.py similarity index 100% rename from core/data_models/information_value.py rename to core/dto/information_value.py diff --git a/core/data_models/modality_color.py b/core/dto/modality_color.py similarity index 100% rename from core/data_models/modality_color.py rename to core/dto/modality_color.py diff --git a/core/data_models/modality_depth.py b/core/dto/modality_depth.py similarity index 100% rename from core/data_models/modality_depth.py rename to core/dto/modality_depth.py diff --git a/core/data_models/modality_lighting.py b/core/dto/modality_lighting.py similarity index 100% rename from core/data_models/modality_lighting.py rename to core/dto/modality_lighting.py diff --git a/core/data_models/point_of_view.py b/core/dto/point_of_view.py similarity index 100% rename from core/data_models/point_of_view.py rename to core/dto/point_of_view.py diff --git a/core/data_models/salience.py b/core/dto/salience.py similarity index 100% rename from core/data_models/salience.py rename to core/dto/salience.py diff --git a/core/data_models/visual_syntax.py b/core/dto/visual_syntax.py similarity index 100% rename from core/data_models/visual_syntax.py rename to core/dto/visual_syntax.py From 50756450e400fae688b990d9ebccfa2e873bf51f Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:29:01 +0100 Subject: [PATCH 12/47] defined final model --- model/src/models/visual_communication.py | 99 ++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/model/src/models/visual_communication.py b/model/src/models/visual_communication.py index e11a81f..8d666f5 100644 --- a/model/src/models/visual_communication.py +++ b/model/src/models/visual_communication.py @@ -2,9 +2,29 @@ from __future__ import annotations import torch.nn as nn +from .angle import AngleTail +from .contact import ContactTail +from .distance import DistanceTail +from .framing import FramingTail +from .information_value import InformationValueTail +from .modality_color import ModalityColorTail +from .modality_depth import ModalityDepthTail +from .modality_lighting import ModalityLightingTail +from .point_of_view import PointOfViewTail from .resnet18_head import ResNet18Head +from .salience import SalienceTail from .visual_syntax import VisualSyntaxTail -from core.data_models import VisualSyntaxData +from core.dto import AngleData +from core.dto import ContactData +from core.dto import DistanceData +from core.dto import FramingData +from core.dto import InformationValueData +from core.dto import ModalityColorData +from core.dto import ModalityDepthData +from core.dto import ModalityLightingData +from core.dto import PointOfViewData +from core.dto import SalienceData +from core.dto import VisualSyntaxData class VisualCommunicationModel(nn.Module): @@ -13,6 +33,16 @@ class VisualCommunicationModel(nn.Module): # store other models self.resnet_head = ResNet18Head() self.visual_syntax_tail = VisualSyntaxTail() + self.contact_tail = ContactTail() + self.angle_tail = AngleTail() + self.point_of_view_tail = PointOfViewTail() + self.distance_tail = DistanceTail() + self.modality_lighting_tail = ModalityLightingTail() + self.modality_color_tail = ModalityColorTail() + self.modality_depth_tail = ModalityDepthTail() + self.information_value_tail = InformationValueTail() + self.framing_tail = FramingTail() + self.salience_tail = SalienceTail() def forward(self, x): # generate visual representation @@ -20,10 +50,69 @@ class VisualCommunicationModel(nn.Module): # prepare result map results = {} # predict visual syntax - visual_syntax_pred = self.visual_syntax_tail(vis_rep).cpu() - # visual_syntax_pred = float() results['visual_syntax'] = VisualSyntaxData.from_list( - visual_syntax_pred, + self.visual_syntax_tail( + vis_rep, + ).cpu(), + ) + # predict contact + results['contact'] = ContactData.from_list( + self.contact_tail( + vis_rep, + ).cpu(), + ) + # predict angle + results['angle'] = AngleData.from_list( + self.angle_tail( + vis_rep, + ).cpu(), + ) + # predict point of view + results['point_of_view'] = PointOfViewData.from_list( + self.point_of_view_tail( + vis_rep, + ).cpu(), + ) + # predict distance + results['distance'] = DistanceData.from_list( + self.distance_tail( + vis_rep, + ).cpu(), + ) + # predict modality lighting + results['modality_lighting'] = ModalityLightingData.from_list( + self.modality_lighting_tail( + vis_rep, + ).cpu(), + ) + # predict modality color + results['modality_color'] = ModalityColorData.from_list( + self.modality_color_tail( + vis_rep, + ).cpu(), + ) + # predict modality depth + results['modality_depth'] = ModalityDepthData.from_list( + self.modality_depth_tail( + vis_rep, + ).cpu(), + ) + # predict information value + results['information_value'] = InformationValueData.from_list( + self.information_value_tail( + vis_rep, + ).cpu(), + ) + # predict framing + results['framing'] = FramingData.from_list( + self.framing_tail( + vis_rep, + ).cpu(), + ) + # predict salience + results['salience'] = SalienceData.from_list( + self.salience_tail( + vis_rep, + ).cpu(), ) - return results From eceb6fba9e8f5087d0698badae573398526c242e Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:29:21 +0100 Subject: [PATCH 13/47] exposed model --- model/src/models/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model/src/models/__init__.py b/model/src/models/__init__.py index e69de29..1f69980 100644 --- a/model/src/models/__init__.py +++ b/model/src/models/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .visual_communication import VisualCommunicationModel From 7b3782a1ce7a060785f52177118650e29ee65ca6 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:38:20 +0100 Subject: [PATCH 14/47] renamed file for clarity --- core/dto/{data_model.py => base_data_model.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/dto/{data_model.py => base_data_model.py} (100%) diff --git a/core/dto/data_model.py b/core/dto/base_data_model.py similarity index 100% rename from core/dto/data_model.py rename to core/dto/base_data_model.py From 5651c9d2008889519a3aff850f14712c8185e2ec Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:40:55 +0100 Subject: [PATCH 15/47] renamed file for clarity --- core/dto/base_data_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/dto/base_data_model.py b/core/dto/base_data_model.py index 9650328..39cd637 100644 --- a/core/dto/base_data_model.py +++ b/core/dto/base_data_model.py @@ -6,7 +6,7 @@ from pydantic import BaseModel from pydantic import ValidationError -class DataModel(BaseModel): +class BaseDataModel(BaseModel): @classmethod def classname(cls) -> str: From ccf2abda63053979280f63178ad7899ea220322b Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:42:01 +0100 Subject: [PATCH 16/47] updated to new base data model name --- core/dto/angle.py | 4 ++-- core/dto/contact.py | 4 ++-- core/dto/distance.py | 4 ++-- core/dto/framing.py | 4 ++-- core/dto/information_value.py | 4 ++-- core/dto/modality_color.py | 4 ++-- core/dto/modality_depth.py | 4 ++-- core/dto/modality_lighting.py | 4 ++-- core/dto/point_of_view.py | 4 ++-- core/dto/salience.py | 4 ++-- core/dto/visual_syntax.py | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/core/dto/angle.py b/core/dto/angle.py index 4af953c..4d1e226 100644 --- a/core/dto/angle.py +++ b/core/dto/angle.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class AngleData(DataModel): +class AngleData(BaseDataModel): high: float eye_level: float low: float diff --git a/core/dto/contact.py b/core/dto/contact.py index 79f8909..a1a0fbf 100644 --- a/core/dto/contact.py +++ b/core/dto/contact.py @@ -1,8 +1,8 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class ContactData(DataModel): +class ContactData(BaseDataModel): offer: float demand: float diff --git a/core/dto/distance.py b/core/dto/distance.py index 29236c6..d563119 100644 --- a/core/dto/distance.py +++ b/core/dto/distance.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class DistanceData(DataModel): +class DistanceData(BaseDataModel): long: float medium: float close: float diff --git a/core/dto/framing.py b/core/dto/framing.py index 0a572e8..60178b8 100644 --- a/core/dto/framing.py +++ b/core/dto/framing.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class FramingData(DataModel): +class FramingData(BaseDataModel): frame_lines: float empty_space: float colour_contrast: float diff --git a/core/dto/information_value.py b/core/dto/information_value.py index 0e8ef23..5b3e19f 100644 --- a/core/dto/information_value.py +++ b/core/dto/information_value.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class InformationValueData(DataModel): +class InformationValueData(BaseDataModel): given_new: float ideal_real: float central_marginal: float diff --git a/core/dto/modality_color.py b/core/dto/modality_color.py index cad5b1a..f3131d8 100644 --- a/core/dto/modality_color.py +++ b/core/dto/modality_color.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class ModalityColorData(DataModel): +class ModalityColorData(BaseDataModel): high: float medium: float low: float diff --git a/core/dto/modality_depth.py b/core/dto/modality_depth.py index 04dda66..b198b50 100644 --- a/core/dto/modality_depth.py +++ b/core/dto/modality_depth.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class ModalityDepthData(DataModel): +class ModalityDepthData(BaseDataModel): high: float medium: float low: float diff --git a/core/dto/modality_lighting.py b/core/dto/modality_lighting.py index 1ff31ec..e114e57 100644 --- a/core/dto/modality_lighting.py +++ b/core/dto/modality_lighting.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class ModalityLightingData(DataModel): +class ModalityLightingData(BaseDataModel): high: float medium: float low: float diff --git a/core/dto/point_of_view.py b/core/dto/point_of_view.py index 12a2cbe..f46279a 100644 --- a/core/dto/point_of_view.py +++ b/core/dto/point_of_view.py @@ -1,8 +1,8 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class PointOfViewData(DataModel): +class PointOfViewData(BaseDataModel): frontal: float oblique: float diff --git a/core/dto/salience.py b/core/dto/salience.py index 40b73eb..2ae76f7 100644 --- a/core/dto/salience.py +++ b/core/dto/salience.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class SalienceData(DataModel): +class SalienceData(BaseDataModel): size: float colour: float tone: float diff --git a/core/dto/visual_syntax.py b/core/dto/visual_syntax.py index 760638f..bf584d3 100644 --- a/core/dto/visual_syntax.py +++ b/core/dto/visual_syntax.py @@ -1,9 +1,9 @@ from __future__ import annotations -from .data_model import DataModel +from .base_data_model import BaseDataModel -class VisualSyntaxData(DataModel): +class VisualSyntaxData(BaseDataModel): non_transactional_action: float non_transactional_reaction: float unidirectional_transactional_action: float From 68c2bb56d5589b1ac896aac1e542d17a0512598a Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:47:50 +0100 Subject: [PATCH 17/47] added definition of final model data --- core/dto/model_data.py | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 core/dto/model_data.py diff --git a/core/dto/model_data.py b/core/dto/model_data.py new file mode 100644 index 0000000..a2baa75 --- /dev/null +++ b/core/dto/model_data.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from pydantic import BaseModel + +from .angle import AngleData +from .contact import ContactData +from .distance import DistanceData +from .framing import FramingData +from .information_value import InformationValueData +from .modality_color import ModalityColorData +from .modality_depth import ModalityDepthData +from .modality_lighting import ModalityLightingData +from .point_of_view import PointOfViewData +from .salience import SalienceData +from .visual_syntax import VisualSyntaxData + + +class ModelData(BaseModel): + visual_syntax: VisualSyntaxData + contact: ContactData + angle: AngleData + point_of_view: PointOfViewData + distance: DistanceData + modality_lighting: ModalityLightingData + modality_color: ModalityColorData + modality_depth: ModalityDepthData + information_value: InformationValueData + framing: FramingData + salience: SalienceData + + @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) -> ModelData: + """Instantiate with random numbers.""" + kwargs = { + field: field_info.annotation.from_random() # type: ignore + for field, field_info + in cls.model_fields.items() + } + return cls(**kwargs) From 5021cfec5175be86d9b084ae2622c94c9edbb552 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 10:50:22 +0100 Subject: [PATCH 18/47] renamed file for clarity --- core/database/__init__.py | 2 +- core/database/{database.py => base.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename core/database/{database.py => base.py} (100%) diff --git a/core/database/__init__.py b/core/database/__init__.py index e964218..2524165 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -1,9 +1,9 @@ from __future__ import annotations +from .base import connect from .classes import ModelOutputs from .classes import NoDocumentFoundException from .classes import VisualCommunication -from .database import connect from .utils import get_visual_communication from .utils import list_names from .utils import total_annotated diff --git a/core/database/database.py b/core/database/base.py similarity index 100% rename from core/database/database.py rename to core/database/base.py From 7f906068d730419e97d855b4eb994a7bcb344e75 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 18:15:52 +0100 Subject: [PATCH 19/47] exposed final model data object --- core/dto/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/dto/__init__.py b/core/dto/__init__.py index a0b2400..9244cbf 100644 --- a/core/dto/__init__.py +++ b/core/dto/__init__.py @@ -8,6 +8,7 @@ from .information_value import InformationValueData from .modality_color import ModalityColorData from .modality_depth import ModalityDepthData from .modality_lighting import ModalityLightingData +from .model_data import ModelData from .point_of_view import PointOfViewData from .salience import SalienceData from .visual_syntax import VisualSyntaxData From a7f2f67d0579c189082a893e1592d047605398db Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 18:17:00 +0100 Subject: [PATCH 20/47] removed unused object --- core/database/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/database/__init__.py b/core/database/__init__.py index 2524165..2ae7dd7 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -1,7 +1,6 @@ from __future__ import annotations from .base import connect -from .classes import ModelOutputs from .classes import NoDocumentFoundException from .classes import VisualCommunication from .utils import get_visual_communication From 6fec85adfe99cd5c541e3d661a40051d2ad5bcfb Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 18:19:29 +0100 Subject: [PATCH 21/47] removed unused object --- core/database/classes.py | 91 ++-------------------------------------- core/database/utils.py | 6 +-- 2 files changed, 7 insertions(+), 90 deletions(-) diff --git a/core/database/classes.py b/core/database/classes.py index 165e949..b4f1714 100644 --- a/core/database/classes.py +++ b/core/database/classes.py @@ -11,101 +11,18 @@ from pydantic import BaseModel from pydantic import field_serializer from pydantic import field_validator -from src.model_experiential import ( - VisualSyntaxModelOutput, -) -from src.model_interpersonal import AngleModelOutput -from src.model_interpersonal import ContactModelOutput -from src.model_interpersonal import DistanceModelOutput -from src.model_interpersonal import ModalityColorModelOutput -from src.model_interpersonal import ModalityDepthModelOutput -from src.model_interpersonal import ModalityLightingModelOutput -from src.model_interpersonal import PointOfViewModelOutput -from src.model_textual import FramingModelOutput -from src.model_textual import InformationValueModelOutput -from src.model_textual import SalienceModelOutput +from core.dto import ModelData class NoDocumentFoundException(Exception): pass -class ModelOutputs(BaseModel): - visual_syntax: VisualSyntaxModelOutput - contact: ContactModelOutput - angle: AngleModelOutput - point_of_view: PointOfViewModelOutput - distance: DistanceModelOutput - modality_lighting: ModalityLightingModelOutput - modality_color: ModalityColorModelOutput - modality_depth: ModalityDepthModelOutput - information_value: InformationValueModelOutput - framing: FramingModelOutput - salience: SalienceModelOutput - - @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) -> ModelOutputs: - """Instantiate with random numbers.""" - kwargs = { - field: field_info.annotation.from_random() # type: ignore - for field, field_info - in cls.model_fields.items() - } - return cls(**kwargs) - - @classmethod - def from_annotations( - cls, - visual_syntax: str, - contact: str, - angle: str, - point_of_view: str, - distance: str, - modality_lighting: str, - modality_color: str, - modality_depth: str, - information_value: str, - framing: str, - salience: str, - ) -> ModelOutputs: - """Instantiate from annotation.""" - kwargs = { - 'visual_syntax': VisualSyntaxModelOutput - .from_choice(visual_syntax), - 'contact': ContactModelOutput - .from_choice(contact), - 'angle': AngleModelOutput - .from_choice(angle), - 'point_of_view': PointOfViewModelOutput - .from_choice(point_of_view), - 'distance': DistanceModelOutput - .from_choice(distance), - 'modality_lighting': ModalityLightingModelOutput - .from_choice(modality_lighting), - 'modality_color': ModalityColorModelOutput - .from_choice(modality_color), - 'modality_depth': ModalityDepthModelOutput - .from_choice(modality_depth), - 'information_value': InformationValueModelOutput - .from_choice(information_value), - 'framing': FramingModelOutput - .from_choice(framing), - 'salience': SalienceModelOutput - .from_choice(salience), - } - return cls(**kwargs) - - class VisualCommunication(BaseModel): name: str image: Image.Image - annotation: ModelOutputs | None = None - prediction: ModelOutputs | None = None + annotation: ModelData | None = None + prediction: ModelData | None = None class Config: arbitrary_types_allowed = True @@ -162,4 +79,4 @@ class VisualCommunication(BaseModel): """Generate random prediction values.""" if not force and self.prediction is not None: logging.warning('set force=True to overwrite existing values.') - self.prediction = ModelOutputs.from_random() + self.prediction = ModelData.from_random() diff --git a/core/database/utils.py b/core/database/utils.py index 581b807..e44317e 100644 --- a/core/database/utils.py +++ b/core/database/utils.py @@ -4,9 +4,9 @@ import logging from pymongo.collection import Collection -from .classes import ModelOutputs from .classes import NoDocumentFoundException from .classes import VisualCommunication +from core.dto import ModelData def count_documents( @@ -97,7 +97,7 @@ def get_visual_communication( def upsert_predictions( collection: Collection, vis_com_name: str, - predictions: ModelOutputs, + predictions: ModelData, ) -> None: """Upsert prediction data in the database.""" query = { @@ -120,7 +120,7 @@ def upsert_predictions( def upsert_annotations( collection: Collection, vis_com_name: str, - annotations: ModelOutputs, + annotations: ModelData, ) -> None: """Upserts annotation data in the database.""" query = { From 8965eb7192b3212004e83fb52db66cd10fb66a98 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 17 Mar 2024 18:41:05 +0100 Subject: [PATCH 22/47] moved object definitions into separate files --- core/database/classes/__init__.py | 4 ++++ core/database/classes/exceptions.py | 5 +++++ .../database/{classes.py => classes/visual_communication.py} | 4 ---- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 core/database/classes/__init__.py create mode 100644 core/database/classes/exceptions.py rename core/database/{classes.py => classes/visual_communication.py} (97%) diff --git a/core/database/classes/__init__.py b/core/database/classes/__init__.py new file mode 100644 index 0000000..ae5adfb --- /dev/null +++ b/core/database/classes/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .exceptions import NoDocumentFoundException +from .visual_communication import VisualCommunication diff --git a/core/database/classes/exceptions.py b/core/database/classes/exceptions.py new file mode 100644 index 0000000..56d1194 --- /dev/null +++ b/core/database/classes/exceptions.py @@ -0,0 +1,5 @@ +from __future__ import annotations + + +class NoDocumentFoundException(Exception): + pass diff --git a/core/database/classes.py b/core/database/classes/visual_communication.py similarity index 97% rename from core/database/classes.py rename to core/database/classes/visual_communication.py index b4f1714..2f6b44b 100644 --- a/core/database/classes.py +++ b/core/database/classes/visual_communication.py @@ -14,10 +14,6 @@ from pydantic import field_validator from core.dto import ModelData -class NoDocumentFoundException(Exception): - pass - - class VisualCommunication(BaseModel): name: str image: Image.Image From 080c7e7a5c5378c8f6112ddff72d249a272a2a88 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 18:46:33 +0100 Subject: [PATCH 23/47] fixed bug when extracting names from list --- core/database/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/database/utils.py b/core/database/utils.py index e44317e..65f5151 100644 --- a/core/database/utils.py +++ b/core/database/utils.py @@ -37,6 +37,7 @@ def list_names( query = {} if has_annotation: query['annotation'] = {'$ne': None} + # execute query res_list = collection.find( filter=query, projection={ @@ -44,7 +45,9 @@ def list_names( 'name': True, }, ) - return list(res_list) + # extract information + name_list = [elem['name'] for elem in res_list] + return name_list def total_documents( From 657e9c4b1dc48880004612fe975b8af4b9217913 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 19:13:31 +0100 Subject: [PATCH 24/47] updated imports --- tests/generate_random_prediction_test.py | 2 +- tests/get_visual_communication_test.py | 5 +++-- tests/image_download_test.py | 5 +++-- tests/image_upload_test.py | 5 +++-- tests/image_upload_to_server_test.py | 4 ++-- tests/model_outputs_from_annotation_test.py | 4 ++-- tests/prediction_upload_test.py | 7 ++++--- tests/total_annotated_test.py | 9 ++++++--- tests/total_documents_test.py | 9 ++++++--- 9 files changed, 30 insertions(+), 20 deletions(-) diff --git a/tests/generate_random_prediction_test.py b/tests/generate_random_prediction_test.py index ead903a..c97e54c 100644 --- a/tests/generate_random_prediction_test.py +++ b/tests/generate_random_prediction_test.py @@ -2,7 +2,7 @@ from __future__ import annotations from pathlib import Path -from database import VisualCommunication +from core.database.classes import VisualCommunication if __name__ == '__main__': diff --git a/tests/get_visual_communication_test.py b/tests/get_visual_communication_test.py index f701f2a..6cd29cc 100644 --- a/tests/get_visual_communication_test.py +++ b/tests/get_visual_communication_test.py @@ -3,10 +3,11 @@ from __future__ import annotations import os from pathlib import Path -from database import connect -from database import get_visual_communication from dotenv import load_dotenv +from core.database import connect +from core.database import get_visual_communication + if __name__ == '__main__': # prepare env vars env_path = Path(__file__).parent.parent / 'local.env' diff --git a/tests/image_download_test.py b/tests/image_download_test.py index 76a166f..8f94244 100644 --- a/tests/image_download_test.py +++ b/tests/image_download_test.py @@ -3,10 +3,11 @@ from __future__ import annotations import os from pathlib import Path -from database import connect -from database import VisualCommunication from dotenv import load_dotenv +from core.database import connect +from core.database.classes import VisualCommunication + if __name__ == '__main__': # prepare env vars env_path = Path(__file__).parent.parent / 'local.env' diff --git a/tests/image_upload_test.py b/tests/image_upload_test.py index 30447e7..4a99c71 100644 --- a/tests/image_upload_test.py +++ b/tests/image_upload_test.py @@ -3,11 +3,12 @@ from __future__ import annotations import os from pathlib import Path -from database import connect -from database import VisualCommunication from dotenv import load_dotenv from pymongo.errors import DuplicateKeyError +from core.database import connect +from core.database.classes import VisualCommunication + if __name__ == '__main__': # get list of image paths test_dir = Path(__file__).parent diff --git a/tests/image_upload_to_server_test.py b/tests/image_upload_to_server_test.py index 3ce29b2..5db75e2 100644 --- a/tests/image_upload_to_server_test.py +++ b/tests/image_upload_to_server_test.py @@ -5,8 +5,8 @@ from pathlib import Path from dotenv import load_dotenv from pymongo.errors import DuplicateKeyError -from src.database import connect -from src.database import VisualCommunication +from core.database import connect +from core.database import VisualCommunication if __name__ == '__main__': # get list of image paths diff --git a/tests/model_outputs_from_annotation_test.py b/tests/model_outputs_from_annotation_test.py index e3dc04e..c4844fb 100644 --- a/tests/model_outputs_from_annotation_test.py +++ b/tests/model_outputs_from_annotation_test.py @@ -1,12 +1,12 @@ from __future__ import annotations -from database import ModelOutputs +from core.dto import ModelData if __name__ == '__main__': # instantiate data object vis_com_list = [ - ModelOutputs.from_random() + ModelData.from_random() for i in range(3) ] diff --git a/tests/prediction_upload_test.py b/tests/prediction_upload_test.py index e517b84..94246c0 100644 --- a/tests/prediction_upload_test.py +++ b/tests/prediction_upload_test.py @@ -4,11 +4,12 @@ import logging import os from pathlib import Path -from database import connect -from database import upsert_predictions -from database import VisualCommunication from dotenv import load_dotenv +from core.database import connect +from core.database import upsert_predictions +from core.database.classes import VisualCommunication + if __name__ == '__main__': # setup logging fmt = ( diff --git a/tests/total_annotated_test.py b/tests/total_annotated_test.py index 355a90d..0dafadb 100644 --- a/tests/total_annotated_test.py +++ b/tests/total_annotated_test.py @@ -3,10 +3,11 @@ from __future__ import annotations import os from pathlib import Path -from database import connect -from database import total_annotated from dotenv import load_dotenv +from core.database import connect +from core.database import total_documents + if __name__ == '__main__': # prepare env vars env_path = Path(__file__).parent.parent / 'local.env' @@ -16,5 +17,7 @@ if __name__ == '__main__': # connect to database collection, db, client = connect() # get visual communication - num_docs = total_annotated(collection) + num_docs = total_documents( + collection=collection, + ) print(f"number of annotated documents in database: {num_docs}") diff --git a/tests/total_documents_test.py b/tests/total_documents_test.py index 7a150f4..e6e04a7 100644 --- a/tests/total_documents_test.py +++ b/tests/total_documents_test.py @@ -3,10 +3,11 @@ from __future__ import annotations import os from pathlib import Path -from database import connect -from database import total_documents from dotenv import load_dotenv +from core.database import connect +from core.database import total_documents + if __name__ == '__main__': # prepare env vars env_path = Path(__file__).parent.parent / 'local.env' @@ -16,5 +17,7 @@ if __name__ == '__main__': # connect to database collection, db, client = connect() # get visual communication - num_docs = total_documents(collection) + num_docs = total_documents( + collection=collection, + ) print(f"total number of documents in database: {num_docs}") From dc9f611e17c7f1572eba0e25ef74c2813c7b3c41 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 19:18:57 +0100 Subject: [PATCH 25/47] moved functions into separate files --- core/database/utils.py | 161 ------------------ core/database/utils/__init__.py | 8 + core/database/utils/count_documents.py | 20 +++ .../utils/get_visual_communication.py | 36 ++++ core/database/utils/list_names.py | 27 +++ core/database/utils/upsert_annotation.py | 30 ++++ core/database/utils/upsert_prediction.py | 30 ++++ .../utils/upsert_visual_communication.py | 23 +++ 8 files changed, 174 insertions(+), 161 deletions(-) delete mode 100644 core/database/utils.py create mode 100644 core/database/utils/__init__.py create mode 100644 core/database/utils/count_documents.py create mode 100644 core/database/utils/get_visual_communication.py create mode 100644 core/database/utils/list_names.py create mode 100644 core/database/utils/upsert_annotation.py create mode 100644 core/database/utils/upsert_prediction.py create mode 100644 core/database/utils/upsert_visual_communication.py diff --git a/core/database/utils.py b/core/database/utils.py deleted file mode 100644 index 65f5151..0000000 --- a/core/database/utils.py +++ /dev/null @@ -1,161 +0,0 @@ -from __future__ import annotations - -import logging - -from pymongo.collection import Collection - -from .classes import NoDocumentFoundException -from .classes import VisualCommunication -from core.dto import ModelData - - -def count_documents( - collection: Collection, - has_annotation: bool = False, -) -> int: - """ - Get the total number of documents - in database that matches the filters. - """ - assert isinstance(collection, Collection) - assert isinstance(has_annotation, bool) - # build query - query = {} - if has_annotation: - query['annotation'] = {'$ne': None} - return collection.count_documents(filter=query) - - -def list_names( - collection: Collection, - has_annotation: bool = True, -) -> list[str]: - """List the names of entries that match the filters.""" - assert isinstance(collection, Collection) - assert isinstance(has_annotation, bool) - # build query - query = {} - if has_annotation: - query['annotation'] = {'$ne': None} - # execute query - res_list = collection.find( - filter=query, - projection={ - '_id': False, - 'name': True, - }, - ) - # extract information - name_list = [elem['name'] for elem in res_list] - return name_list - - -def total_documents( - collection: Collection, -) -> int: - """Get total number of documents in database.""" - return collection.count_documents(filter={}) - - -def total_annotated( - collection: Collection, -) -> int: - """Get total number of annotated documents in database.""" - query = { - 'annotation': { - '$ne': None, - }, - } - return collection.count_documents(filter=query) - - -def get_visual_communication( - collection: Collection, - with_annotation: bool = False, -) -> VisualCommunication: - """Get a random visual communication from the database.""" - query = {} - if with_annotation: - query['annotation'] = {'$ne': None} - else: - query['annotation'] = {'$eq': None} - data = collection.aggregate([ - { - '$match': query, # find using filters - }, - { - '$sample': { - 'size': 1, # get one random - }, - }, - ]) - data_list = list(data) # read data from cursor object - if len(data_list) == 0: - logging.error('failed getting visual communication') - raise NoDocumentFoundException() - logging.info('finished') - return VisualCommunication.model_validate(data_list[0]) - - -def upsert_predictions( - collection: Collection, - vis_com_name: str, - predictions: ModelData, -) -> None: - """Upsert prediction data in the database.""" - query = { - 'name': vis_com_name, - } - update = { - '$set': { - 'prediction': predictions.model_dump(), - }, - } - res = collection.update_one( - filter=query, - update=update, - upsert=True, - ) - logging.debug('upserted document: %s', res) - logging.info('finished') - - -def upsert_annotations( - collection: Collection, - vis_com_name: str, - annotations: ModelData, -) -> None: - """Upserts annotation data in the database.""" - query = { - 'name': vis_com_name, - } - update = { - '$set': { - 'annotation': annotations.model_dump(), - }, - } - res = collection.update_one( - filter=query, - update=update, - upsert=True, - ) - logging.info('upserted document: %s', res) - logging.info('finished') - - -def upsert_visual_communication( - collection: Collection, - visual_communication_list: list[VisualCommunication], -) -> bool: - """ - Upsert VisualCommunication object in the database. - Returns bool stating success. - """ - response = collection.insert_many( - [ - vis_com.model_dump() - for vis_com - in visual_communication_list - ], - ) - return response.acknowledged diff --git a/core/database/utils/__init__.py b/core/database/utils/__init__.py new file mode 100644 index 0000000..b22e0e6 --- /dev/null +++ b/core/database/utils/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .count_documents import count_documents +from .get_visual_communication import get_visual_communication +from .list_names import list_names +from .upsert_annotation import upsert_annotations +from .upsert_prediction import upsert_predictions +from .upsert_visual_communication import upsert_visual_communication diff --git a/core/database/utils/count_documents.py b/core/database/utils/count_documents.py new file mode 100644 index 0000000..1f74278 --- /dev/null +++ b/core/database/utils/count_documents.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from pymongo.collection import Collection + + +def count_documents( + collection: Collection, + has_annotation: bool = False, +) -> int: + """ + Get the total number of documents + in database that matches the filters. + """ + assert isinstance(collection, Collection) + assert isinstance(has_annotation, bool) + # build query + query = {} + if has_annotation: + query['annotation'] = {'$ne': None} + return collection.count_documents(filter=query) diff --git a/core/database/utils/get_visual_communication.py b/core/database/utils/get_visual_communication.py new file mode 100644 index 0000000..0add874 --- /dev/null +++ b/core/database/utils/get_visual_communication.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import logging + +from pymongo.collection import Collection + +from core.database import NoDocumentFoundException +from core.database import VisualCommunication + + +def get_visual_communication( + collection: Collection, + with_annotation: bool = False, +) -> VisualCommunication: + """Get a random visual communication from the database.""" + query = {} + if with_annotation: + query['annotation'] = {'$ne': None} + else: + query['annotation'] = {'$eq': None} + data = collection.aggregate([ + { + '$match': query, # find using filters + }, + { + '$sample': { + 'size': 1, # get one random + }, + }, + ]) + data_list = list(data) # read data from cursor object + if len(data_list) == 0: + logging.error('failed getting visual communication') + raise NoDocumentFoundException() + logging.info('finished') + return VisualCommunication.model_validate(data_list[0]) diff --git a/core/database/utils/list_names.py b/core/database/utils/list_names.py new file mode 100644 index 0000000..e9cd624 --- /dev/null +++ b/core/database/utils/list_names.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from pymongo.collection import Collection + + +def list_names( + collection: Collection, + has_annotation: bool = True, +) -> list[str]: + """List the names of entries that match the filters.""" + assert isinstance(collection, Collection) + assert isinstance(has_annotation, bool) + # build query + query = {} + if has_annotation: + query['annotation'] = {'$ne': None} + # execute query + res_list = collection.find( + filter=query, + projection={ + '_id': False, + 'name': True, + }, + ) + # extract information + name_list = [elem['name'] for elem in res_list] + return name_list diff --git a/core/database/utils/upsert_annotation.py b/core/database/utils/upsert_annotation.py new file mode 100644 index 0000000..54be9eb --- /dev/null +++ b/core/database/utils/upsert_annotation.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import logging + +from pymongo.collection import Collection + +from core.dto import ModelData + + +def upsert_annotations( + collection: Collection, + vis_com_name: str, + annotations: ModelData, +) -> None: + """Upserts annotation data in the database.""" + query = { + 'name': vis_com_name, + } + update = { + '$set': { + 'annotation': annotations.model_dump(), + }, + } + res = collection.update_one( + filter=query, + update=update, + upsert=True, + ) + logging.info('upserted document: %s', res) + logging.info('finished') diff --git a/core/database/utils/upsert_prediction.py b/core/database/utils/upsert_prediction.py new file mode 100644 index 0000000..2b1cd1a --- /dev/null +++ b/core/database/utils/upsert_prediction.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import logging + +from pymongo.collection import Collection + +from core.dto import ModelData + + +def upsert_predictions( + collection: Collection, + vis_com_name: str, + predictions: ModelData, +) -> None: + """Upsert prediction data in the database.""" + query = { + 'name': vis_com_name, + } + update = { + '$set': { + 'prediction': predictions.model_dump(), + }, + } + res = collection.update_one( + filter=query, + update=update, + upsert=True, + ) + logging.debug('upserted document: %s', res) + logging.info('finished') diff --git a/core/database/utils/upsert_visual_communication.py b/core/database/utils/upsert_visual_communication.py new file mode 100644 index 0000000..6744e01 --- /dev/null +++ b/core/database/utils/upsert_visual_communication.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from pymongo.collection import Collection + +from core.database import VisualCommunication + + +def upsert_visual_communication( + collection: Collection, + visual_communication_list: list[VisualCommunication], +) -> bool: + """ + Upsert VisualCommunication object in the database. + Returns bool stating success. + """ + response = collection.insert_many( + [ + vis_com.model_dump() + for vis_com + in visual_communication_list + ], + ) + return response.acknowledged From 133019ad22472be8445f2cec1bd703f55f66f5df Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 19:22:39 +0100 Subject: [PATCH 26/47] added init file --- core/__init__.py | 0 core/database/__init__.py | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 core/__init__.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/database/__init__.py b/core/database/__init__.py index 2ae7dd7..238806b 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -3,10 +3,9 @@ from __future__ import annotations from .base import connect from .classes import NoDocumentFoundException from .classes import VisualCommunication +from .utils import count_documents from .utils import get_visual_communication from .utils import list_names -from .utils import total_annotated -from .utils import total_documents from .utils import upsert_annotations from .utils import upsert_predictions from .utils import upsert_visual_communication From dc976540bfb5d018b2c87d4fd4f6d877eed2a8d2 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 19:29:05 +0100 Subject: [PATCH 27/47] added definition of dataset --- core/database/classes/dataset.py | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 core/database/classes/dataset.py diff --git a/core/database/classes/dataset.py b/core/database/classes/dataset.py new file mode 100644 index 0000000..cea1d16 --- /dev/null +++ b/core/database/classes/dataset.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import random + +from pydantic import BaseModel + + +class Dataset(BaseModel): + train_names: list[str] + test_names: list[str] + validation_names: list[str] + + @classmethod + def fraction_map(cls) -> dict[str, float]: + """Dict with train, test and validation fractions.""" + # define map + split_map = { + 'train': 0.7, + 'test': 0.2, + 'validation': 0.1, + } + # sanity check + assert sum(split_map.values()) == 1.0 + return split_map + + @classmethod + def new_from_name_list( + cls, + name_list: list[str], + ) -> Dataset: + """Generate new dataset from list of filenames.""" + # calculate split fractions + fraction_map = cls.fraction_map() + num_total = len(name_list) + num_validation = round(num_total * fraction_map['validation']) + num_test = round(num_total * fraction_map['test']) + # split data + validation_name_list = random.choices(name_list, k=num_validation) + name_list = [ + name for name in name_list if name not in validation_name_list + ] + test_name_list = random.choices(name_list, k=num_test) + train_name_list = [ + name for name in name_list if name not in test_name_list + ] + # instantiate object + dataset = Dataset( + train_names=train_name_list, + test_names=test_name_list, + validation_names=validation_name_list, + ) + return dataset From 167f9ecb3ccd704c172c66c80928e14a8d34a677 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 20 Mar 2024 19:45:07 +0100 Subject: [PATCH 28/47] added create_time factory --- core/database/classes/dataset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/database/classes/dataset.py b/core/database/classes/dataset.py index cea1d16..ff3f4aa 100644 --- a/core/database/classes/dataset.py +++ b/core/database/classes/dataset.py @@ -1,11 +1,15 @@ from __future__ import annotations import random +from datetime import datetime +from datetime import UTC from pydantic import BaseModel +from pydantic import Field class Dataset(BaseModel): + create_time: datetime = Field(default_factory=lambda: datetime.now(UTC)) train_names: list[str] test_names: list[str] validation_names: list[str] From 28c9d12a5bf8b2e3d8bdc36d1da974aad629d70d Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 24 Mar 2024 16:37:57 +0100 Subject: [PATCH 29/47] exposed Dataset object --- core/database/classes/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/database/classes/__init__.py b/core/database/classes/__init__.py index ae5adfb..295e6f2 100644 --- a/core/database/classes/__init__.py +++ b/core/database/classes/__init__.py @@ -1,4 +1,5 @@ from __future__ import annotations +from .dataset import Dataset from .exceptions import NoDocumentFoundException from .visual_communication import VisualCommunication From e0eb1dd53c35d1ba919e91259d42ef27c9534503 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 10:28:25 +0200 Subject: [PATCH 30/47] defined data models --- core/data_models/__init__.py | 14 ++++++ core/data_models/angle.py | 11 +++++ core/data_models/contact.py | 11 +++++ core/data_models/data_model.py | 67 +++++++++++++++++++++++++++ core/data_models/distance.py | 12 +++++ core/data_models/framing.py | 13 ++++++ core/data_models/information_value.py | 12 +++++ core/data_models/modality_color.py | 12 +++++ core/data_models/modality_depth.py | 12 +++++ core/data_models/modality_lighting.py | 12 +++++ core/data_models/point_of_view.py | 11 +++++ core/data_models/salience.py | 14 ++++++ core/data_models/visual_syntax.py | 27 +++++++++++ 13 files changed, 228 insertions(+) create mode 100644 core/data_models/__init__.py create mode 100644 core/data_models/angle.py create mode 100644 core/data_models/contact.py create mode 100644 core/data_models/data_model.py create mode 100644 core/data_models/distance.py create mode 100644 core/data_models/framing.py create mode 100644 core/data_models/information_value.py create mode 100644 core/data_models/modality_color.py create mode 100644 core/data_models/modality_depth.py create mode 100644 core/data_models/modality_lighting.py create mode 100644 core/data_models/point_of_view.py create mode 100644 core/data_models/salience.py create mode 100644 core/data_models/visual_syntax.py diff --git a/core/data_models/__init__.py b/core/data_models/__init__.py new file mode 100644 index 0000000..650a187 --- /dev/null +++ b/core/data_models/__init__.py @@ -0,0 +1,14 @@ +"""data_models module content.""" +from __future__ import annotations + +from .angle import AngleData +from .contact import ContactData +from .distance import DistanceData +from .framing import FramingData +from .information_value import InformationValueData +from .modality_color import ModalityColorData +from .modality_depth import ModalityDepthData +from .modality_lighting import ModalityLightingData +from .point_of_view import PointOfViewData +from .salience import SalienceData +from .visual_syntax import VisualSyntaxData diff --git a/core/data_models/angle.py b/core/data_models/angle.py new file mode 100644 index 0000000..5ce2ea0 --- /dev/null +++ b/core/data_models/angle.py @@ -0,0 +1,11 @@ +"""Definition of Angle data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class AngleData(DataModel): + """Angle data model.""" + high: float + eye_level: float + low: float diff --git a/core/data_models/contact.py b/core/data_models/contact.py new file mode 100644 index 0000000..9a5f1cd --- /dev/null +++ b/core/data_models/contact.py @@ -0,0 +1,11 @@ +"""Definition of ContactData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class ContactData(DataModel): + """ContactData data model.""" + + offer: float + demand: float diff --git a/core/data_models/data_model.py b/core/data_models/data_model.py new file mode 100644 index 0000000..70bb79f --- /dev/null +++ b/core/data_models/data_model.py @@ -0,0 +1,67 @@ +"""Definition of DataModel base class.""" +from __future__ import annotations + +import random + +from pydantic import BaseModel +from pydantic import ValidationError + + +class DataModel(BaseModel): + """DataModel base class.""" + + @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) + + @classmethod + def from_list(cls, data_list: list[float]): + """Instantiate from list of values.""" + kwargs = {key: val for key, val in zip(cls.list_fields(), data_list)} + 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()) diff --git a/core/data_models/distance.py b/core/data_models/distance.py new file mode 100644 index 0000000..efd9461 --- /dev/null +++ b/core/data_models/distance.py @@ -0,0 +1,12 @@ +"""Definition of DistanceData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class DistanceData(DataModel): + """DistanceData data model.""" + + long: float + medium: float + close: float diff --git a/core/data_models/framing.py b/core/data_models/framing.py new file mode 100644 index 0000000..2381ebc --- /dev/null +++ b/core/data_models/framing.py @@ -0,0 +1,13 @@ +"""Definition of FramingData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class FramingData(DataModel): + """FramingData data model.""" + + frame_lines: float + empty_space: float + colour_contrast: float + form_contrast: float diff --git a/core/data_models/information_value.py b/core/data_models/information_value.py new file mode 100644 index 0000000..45d10f3 --- /dev/null +++ b/core/data_models/information_value.py @@ -0,0 +1,12 @@ +"""Definition of InformationValueData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class InformationValueData(DataModel): + """InformationValueData data model.""" + + given_new: float + ideal_real: float + central_marginal: float diff --git a/core/data_models/modality_color.py b/core/data_models/modality_color.py new file mode 100644 index 0000000..a34d6be --- /dev/null +++ b/core/data_models/modality_color.py @@ -0,0 +1,12 @@ +"""Definition of ModalityColorData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityColorData(DataModel): + """ModalityColorData data model.""" + + high: float + medium: float + low: float diff --git a/core/data_models/modality_depth.py b/core/data_models/modality_depth.py new file mode 100644 index 0000000..3f8fbe6 --- /dev/null +++ b/core/data_models/modality_depth.py @@ -0,0 +1,12 @@ +"""Definition of ModalityDepthData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityDepthData(DataModel): + """ModalityDepthData data model.""" + + high: float + medium: float + low: float diff --git a/core/data_models/modality_lighting.py b/core/data_models/modality_lighting.py new file mode 100644 index 0000000..fb04f4c --- /dev/null +++ b/core/data_models/modality_lighting.py @@ -0,0 +1,12 @@ +"""Definition of ModalityLightingData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class ModalityLightingData(DataModel): + """ModalityLightingData data model.""" + + high: float + medium: float + low: float diff --git a/core/data_models/point_of_view.py b/core/data_models/point_of_view.py new file mode 100644 index 0000000..98d67c5 --- /dev/null +++ b/core/data_models/point_of_view.py @@ -0,0 +1,11 @@ +"""Definition of PointOfViewData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class PointOfViewData(DataModel): + """PointOfViewData data model.""" + + frontal: float + oblique: float diff --git a/core/data_models/salience.py b/core/data_models/salience.py new file mode 100644 index 0000000..580100d --- /dev/null +++ b/core/data_models/salience.py @@ -0,0 +1,14 @@ +"""Definition of SalienceData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class SalienceData(DataModel): + """SalienceData data model.""" + + size: float + colour: float + tone: float + form: float + positioning: float diff --git a/core/data_models/visual_syntax.py b/core/data_models/visual_syntax.py new file mode 100644 index 0000000..fd38639 --- /dev/null +++ b/core/data_models/visual_syntax.py @@ -0,0 +1,27 @@ +"""Definition of VisualSyntaxData data model.""" +from __future__ import annotations + +from .data_model import DataModel + + +class VisualSyntaxData(DataModel): + """VisualSyntaxData data model.""" + + 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 From 0e404015f1064cc01a72554d1f798f24a093926b Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 10:29:17 +0200 Subject: [PATCH 31/47] commented out app for testing --- docker-compose.local.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 5c237a1..011ee49 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,21 +1,21 @@ version: '3.7' services: - app: - image: visual_critical_discourse_analysis:dev - container_name: visual_critical_discourse_analysis - build: - context: . - dockerfile: Dockerfile - env_file: - - local.env - environment: - - ENV=DEV - ports: - - 8050:8050 - networks: - - backend - depends_on: - - mongo + # app: + # image: visual_critical_discourse_analysis:dev + # container_name: visual_critical_discourse_analysis + # build: + # context: . + # dockerfile: Dockerfile + # env_file: + # - local.env + # environment: + # - ENV=DEV + # ports: + # - 8050:8050 + # networks: + # - backend + # depends_on: + # - mongo mongo: image: mongo:latest container_name: mongo @@ -41,4 +41,4 @@ services: networks: backend: - driver: bridge \ No newline at end of file + driver: bridge From a2164b9ccf98a4e81537ed580d90e804cbef9999 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 10:38:20 +0200 Subject: [PATCH 32/47] renamed files --- core/data_models/__init__.py | 14 ------ core/data_models/angle.py | 11 ----- core/data_models/contact.py | 11 ----- core/data_models/distance.py | 12 ----- core/data_models/framing.py | 13 ----- core/data_models/information_value.py | 12 ----- core/data_models/modality_color.py | 12 ----- core/data_models/modality_depth.py | 12 ----- core/data_models/modality_lighting.py | 12 ----- core/data_models/point_of_view.py | 11 ----- core/data_models/salience.py | 14 ------ core/data_models/visual_syntax.py | 27 ---------- core/dto/__init__.py | 2 +- core/dto/angle.py | 6 ++- core/dto/base_data_model.py | 65 ------------------------- core/dto/contact.py | 7 ++- core/{data_models => dto}/data_model.py | 0 core/dto/distance.py | 7 ++- core/dto/framing.py | 7 ++- core/dto/information_value.py | 7 ++- core/dto/modality_color.py | 7 ++- core/dto/modality_depth.py | 7 ++- core/dto/modality_lighting.py | 7 ++- core/dto/model_data.py | 44 ----------------- core/dto/point_of_view.py | 7 ++- core/dto/salience.py | 7 ++- core/dto/visual_syntax.py | 7 ++- 27 files changed, 55 insertions(+), 293 deletions(-) delete mode 100644 core/data_models/__init__.py delete mode 100644 core/data_models/angle.py delete mode 100644 core/data_models/contact.py delete mode 100644 core/data_models/distance.py delete mode 100644 core/data_models/framing.py delete mode 100644 core/data_models/information_value.py delete mode 100644 core/data_models/modality_color.py delete mode 100644 core/data_models/modality_depth.py delete mode 100644 core/data_models/modality_lighting.py delete mode 100644 core/data_models/point_of_view.py delete mode 100644 core/data_models/salience.py delete mode 100644 core/data_models/visual_syntax.py delete mode 100644 core/dto/base_data_model.py rename core/{data_models => dto}/data_model.py (100%) delete mode 100644 core/dto/model_data.py diff --git a/core/data_models/__init__.py b/core/data_models/__init__.py deleted file mode 100644 index 650a187..0000000 --- a/core/data_models/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -"""data_models module content.""" -from __future__ import annotations - -from .angle import AngleData -from .contact import ContactData -from .distance import DistanceData -from .framing import FramingData -from .information_value import InformationValueData -from .modality_color import ModalityColorData -from .modality_depth import ModalityDepthData -from .modality_lighting import ModalityLightingData -from .point_of_view import PointOfViewData -from .salience import SalienceData -from .visual_syntax import VisualSyntaxData diff --git a/core/data_models/angle.py b/core/data_models/angle.py deleted file mode 100644 index 5ce2ea0..0000000 --- a/core/data_models/angle.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Definition of Angle data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class AngleData(DataModel): - """Angle data model.""" - high: float - eye_level: float - low: float diff --git a/core/data_models/contact.py b/core/data_models/contact.py deleted file mode 100644 index 9a5f1cd..0000000 --- a/core/data_models/contact.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Definition of ContactData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class ContactData(DataModel): - """ContactData data model.""" - - offer: float - demand: float diff --git a/core/data_models/distance.py b/core/data_models/distance.py deleted file mode 100644 index efd9461..0000000 --- a/core/data_models/distance.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Definition of DistanceData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class DistanceData(DataModel): - """DistanceData data model.""" - - long: float - medium: float - close: float diff --git a/core/data_models/framing.py b/core/data_models/framing.py deleted file mode 100644 index 2381ebc..0000000 --- a/core/data_models/framing.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Definition of FramingData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class FramingData(DataModel): - """FramingData data model.""" - - frame_lines: float - empty_space: float - colour_contrast: float - form_contrast: float diff --git a/core/data_models/information_value.py b/core/data_models/information_value.py deleted file mode 100644 index 45d10f3..0000000 --- a/core/data_models/information_value.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Definition of InformationValueData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class InformationValueData(DataModel): - """InformationValueData data model.""" - - given_new: float - ideal_real: float - central_marginal: float diff --git a/core/data_models/modality_color.py b/core/data_models/modality_color.py deleted file mode 100644 index a34d6be..0000000 --- a/core/data_models/modality_color.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Definition of ModalityColorData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class ModalityColorData(DataModel): - """ModalityColorData data model.""" - - high: float - medium: float - low: float diff --git a/core/data_models/modality_depth.py b/core/data_models/modality_depth.py deleted file mode 100644 index 3f8fbe6..0000000 --- a/core/data_models/modality_depth.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Definition of ModalityDepthData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class ModalityDepthData(DataModel): - """ModalityDepthData data model.""" - - high: float - medium: float - low: float diff --git a/core/data_models/modality_lighting.py b/core/data_models/modality_lighting.py deleted file mode 100644 index fb04f4c..0000000 --- a/core/data_models/modality_lighting.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Definition of ModalityLightingData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class ModalityLightingData(DataModel): - """ModalityLightingData data model.""" - - high: float - medium: float - low: float diff --git a/core/data_models/point_of_view.py b/core/data_models/point_of_view.py deleted file mode 100644 index 98d67c5..0000000 --- a/core/data_models/point_of_view.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Definition of PointOfViewData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class PointOfViewData(DataModel): - """PointOfViewData data model.""" - - frontal: float - oblique: float diff --git a/core/data_models/salience.py b/core/data_models/salience.py deleted file mode 100644 index 580100d..0000000 --- a/core/data_models/salience.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Definition of SalienceData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class SalienceData(DataModel): - """SalienceData data model.""" - - size: float - colour: float - tone: float - form: float - positioning: float diff --git a/core/data_models/visual_syntax.py b/core/data_models/visual_syntax.py deleted file mode 100644 index fd38639..0000000 --- a/core/data_models/visual_syntax.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Definition of VisualSyntaxData data model.""" -from __future__ import annotations - -from .data_model import DataModel - - -class VisualSyntaxData(DataModel): - """VisualSyntaxData data model.""" - - 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 diff --git a/core/dto/__init__.py b/core/dto/__init__.py index 9244cbf..650a187 100644 --- a/core/dto/__init__.py +++ b/core/dto/__init__.py @@ -1,3 +1,4 @@ +"""data_models module content.""" from __future__ import annotations from .angle import AngleData @@ -8,7 +9,6 @@ from .information_value import InformationValueData from .modality_color import ModalityColorData from .modality_depth import ModalityDepthData from .modality_lighting import ModalityLightingData -from .model_data import ModelData from .point_of_view import PointOfViewData from .salience import SalienceData from .visual_syntax import VisualSyntaxData diff --git a/core/dto/angle.py b/core/dto/angle.py index 4d1e226..5ce2ea0 100644 --- a/core/dto/angle.py +++ b/core/dto/angle.py @@ -1,9 +1,11 @@ +"""Definition of Angle data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class AngleData(BaseDataModel): +class AngleData(DataModel): + """Angle data model.""" high: float eye_level: float low: float diff --git a/core/dto/base_data_model.py b/core/dto/base_data_model.py deleted file mode 100644 index 39cd637..0000000 --- a/core/dto/base_data_model.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import random - -from pydantic import BaseModel -from pydantic import ValidationError - - -class BaseDataModel(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) - - @classmethod - def from_list(cls, data_list: list[float]): - """Instantiate from list of values.""" - kwargs = {key: val for key, val in zip(cls.list_fields(), data_list)} - 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()) diff --git a/core/dto/contact.py b/core/dto/contact.py index a1a0fbf..9a5f1cd 100644 --- a/core/dto/contact.py +++ b/core/dto/contact.py @@ -1,8 +1,11 @@ +"""Definition of ContactData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class ContactData(BaseDataModel): +class ContactData(DataModel): + """ContactData data model.""" + offer: float demand: float diff --git a/core/data_models/data_model.py b/core/dto/data_model.py similarity index 100% rename from core/data_models/data_model.py rename to core/dto/data_model.py diff --git a/core/dto/distance.py b/core/dto/distance.py index d563119..efd9461 100644 --- a/core/dto/distance.py +++ b/core/dto/distance.py @@ -1,9 +1,12 @@ +"""Definition of DistanceData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class DistanceData(BaseDataModel): +class DistanceData(DataModel): + """DistanceData data model.""" + long: float medium: float close: float diff --git a/core/dto/framing.py b/core/dto/framing.py index 60178b8..2381ebc 100644 --- a/core/dto/framing.py +++ b/core/dto/framing.py @@ -1,9 +1,12 @@ +"""Definition of FramingData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class FramingData(BaseDataModel): +class FramingData(DataModel): + """FramingData data model.""" + frame_lines: float empty_space: float colour_contrast: float diff --git a/core/dto/information_value.py b/core/dto/information_value.py index 5b3e19f..45d10f3 100644 --- a/core/dto/information_value.py +++ b/core/dto/information_value.py @@ -1,9 +1,12 @@ +"""Definition of InformationValueData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class InformationValueData(BaseDataModel): +class InformationValueData(DataModel): + """InformationValueData data model.""" + given_new: float ideal_real: float central_marginal: float diff --git a/core/dto/modality_color.py b/core/dto/modality_color.py index f3131d8..a34d6be 100644 --- a/core/dto/modality_color.py +++ b/core/dto/modality_color.py @@ -1,9 +1,12 @@ +"""Definition of ModalityColorData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class ModalityColorData(BaseDataModel): +class ModalityColorData(DataModel): + """ModalityColorData data model.""" + high: float medium: float low: float diff --git a/core/dto/modality_depth.py b/core/dto/modality_depth.py index b198b50..3f8fbe6 100644 --- a/core/dto/modality_depth.py +++ b/core/dto/modality_depth.py @@ -1,9 +1,12 @@ +"""Definition of ModalityDepthData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class ModalityDepthData(BaseDataModel): +class ModalityDepthData(DataModel): + """ModalityDepthData data model.""" + high: float medium: float low: float diff --git a/core/dto/modality_lighting.py b/core/dto/modality_lighting.py index e114e57..fb04f4c 100644 --- a/core/dto/modality_lighting.py +++ b/core/dto/modality_lighting.py @@ -1,9 +1,12 @@ +"""Definition of ModalityLightingData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class ModalityLightingData(BaseDataModel): +class ModalityLightingData(DataModel): + """ModalityLightingData data model.""" + high: float medium: float low: float diff --git a/core/dto/model_data.py b/core/dto/model_data.py deleted file mode 100644 index a2baa75..0000000 --- a/core/dto/model_data.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from pydantic import BaseModel - -from .angle import AngleData -from .contact import ContactData -from .distance import DistanceData -from .framing import FramingData -from .information_value import InformationValueData -from .modality_color import ModalityColorData -from .modality_depth import ModalityDepthData -from .modality_lighting import ModalityLightingData -from .point_of_view import PointOfViewData -from .salience import SalienceData -from .visual_syntax import VisualSyntaxData - - -class ModelData(BaseModel): - visual_syntax: VisualSyntaxData - contact: ContactData - angle: AngleData - point_of_view: PointOfViewData - distance: DistanceData - modality_lighting: ModalityLightingData - modality_color: ModalityColorData - modality_depth: ModalityDepthData - information_value: InformationValueData - framing: FramingData - salience: SalienceData - - @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) -> ModelData: - """Instantiate with random numbers.""" - kwargs = { - field: field_info.annotation.from_random() # type: ignore - for field, field_info - in cls.model_fields.items() - } - return cls(**kwargs) diff --git a/core/dto/point_of_view.py b/core/dto/point_of_view.py index f46279a..98d67c5 100644 --- a/core/dto/point_of_view.py +++ b/core/dto/point_of_view.py @@ -1,8 +1,11 @@ +"""Definition of PointOfViewData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class PointOfViewData(BaseDataModel): +class PointOfViewData(DataModel): + """PointOfViewData data model.""" + frontal: float oblique: float diff --git a/core/dto/salience.py b/core/dto/salience.py index 2ae76f7..580100d 100644 --- a/core/dto/salience.py +++ b/core/dto/salience.py @@ -1,9 +1,12 @@ +"""Definition of SalienceData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class SalienceData(BaseDataModel): +class SalienceData(DataModel): + """SalienceData data model.""" + size: float colour: float tone: float diff --git a/core/dto/visual_syntax.py b/core/dto/visual_syntax.py index bf584d3..fd38639 100644 --- a/core/dto/visual_syntax.py +++ b/core/dto/visual_syntax.py @@ -1,9 +1,12 @@ +"""Definition of VisualSyntaxData data model.""" from __future__ import annotations -from .base_data_model import BaseDataModel +from .data_model import DataModel -class VisualSyntaxData(BaseDataModel): +class VisualSyntaxData(DataModel): + """VisualSyntaxData data model.""" + non_transactional_action: float non_transactional_reaction: float unidirectional_transactional_action: float From abd83aa5422cc6ec922379395b8092d6ecd380f6 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 11:24:04 +0200 Subject: [PATCH 33/47] added ModelData class --- core/dto/__init__.py | 3 +- core/dto/model_data.py | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 core/dto/model_data.py diff --git a/core/dto/__init__.py b/core/dto/__init__.py index 650a187..be1cd38 100644 --- a/core/dto/__init__.py +++ b/core/dto/__init__.py @@ -1,4 +1,4 @@ -"""data_models module content.""" +"""Data transfer objects module content.""" from __future__ import annotations from .angle import AngleData @@ -9,6 +9,7 @@ from .information_value import InformationValueData from .modality_color import ModalityColorData from .modality_depth import ModalityDepthData from .modality_lighting import ModalityLightingData +from .model_data import ModelData from .point_of_view import PointOfViewData from .salience import SalienceData from .visual_syntax import VisualSyntaxData diff --git a/core/dto/model_data.py b/core/dto/model_data.py new file mode 100644 index 0000000..157f788 --- /dev/null +++ b/core/dto/model_data.py @@ -0,0 +1,82 @@ +"""Definition of ModelData data model.""" +from __future__ import annotations + +from .angle import AngleData +from .contact import ContactData +from .data_model import DataModel +from .distance import DistanceData +from .framing import FramingData +from .information_value import InformationValueData +from .modality_color import ModalityColorData +from .modality_depth import ModalityDepthData +from .modality_lighting import ModalityLightingData +from .point_of_view import PointOfViewData +from .salience import SalienceData +from .visual_syntax import VisualSyntaxData + + +class ModelData(DataModel): + """ModelData model for data IO with combined ML model.""" + visual_syntax: VisualSyntaxData + contact: ContactData + angle: AngleData + point_of_view: PointOfViewData + distance: DistanceData + modality_lighting: ModalityLightingData + modality_color: ModalityColorData + modality_depth: ModalityDepthData + information_value: InformationValueData + framing: FramingData + salience: SalienceData + + @classmethod + def from_random(cls) -> ModelData: + """Instantiate with random numbers.""" + kwargs = { + field: field_info.annotation.from_random() # type: ignore + for field, field_info + in cls.model_fields.items() + } + return cls(**kwargs) + + @classmethod + def from_annotations( + cls, + visual_syntax: str, + contact: str, + angle: str, + point_of_view: str, + distance: str, + modality_lighting: str, + modality_color: str, + modality_depth: str, + information_value: str, + framing: str, + salience: str, + ) -> ModelData: + """Instantiate from annotation.""" + kwargs = { + 'visual_syntax': VisualSyntaxData + .from_choice(visual_syntax), + 'contact': ContactData + .from_choice(contact), + 'angle': AngleData + .from_choice(angle), + 'point_of_view': PointOfViewData + .from_choice(point_of_view), + 'distance': DistanceData + .from_choice(distance), + 'modality_lighting': ModalityLightingData + .from_choice(modality_lighting), + 'modality_color': ModalityColorData + .from_choice(modality_color), + 'modality_depth': ModalityDepthData + .from_choice(modality_depth), + 'information_value': InformationValueData + .from_choice(information_value), + 'framing': FramingData + .from_choice(framing), + 'salience': SalienceData + .from_choice(salience), + } + return cls(**kwargs) From b430a15ecea9d2ee0a2abfd5aa4624f766374b04 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 11:24:28 +0200 Subject: [PATCH 34/47] updated database classes --- core/database/classes/__init__.py | 1 + core/database/classes/dataset.py | 15 +++++++++++++++ core/database/classes/exceptions.py | 3 ++- core/database/classes/visual_communication.py | 11 +++++++++-- 4 files changed, 27 insertions(+), 3 deletions(-) mode change 100644 => 100755 core/database/classes/__init__.py mode change 100644 => 100755 core/database/classes/dataset.py mode change 100644 => 100755 core/database/classes/exceptions.py mode change 100644 => 100755 core/database/classes/visual_communication.py diff --git a/core/database/classes/__init__.py b/core/database/classes/__init__.py old mode 100644 new mode 100755 index 295e6f2..9e260cd --- a/core/database/classes/__init__.py +++ b/core/database/classes/__init__.py @@ -1,3 +1,4 @@ +"""Database classes module content.""" from __future__ import annotations from .dataset import Dataset diff --git a/core/database/classes/dataset.py b/core/database/classes/dataset.py old mode 100644 new mode 100755 index ff3f4aa..0c3fa1a --- a/core/database/classes/dataset.py +++ b/core/database/classes/dataset.py @@ -1,14 +1,18 @@ +"""Definition of database Dataset class.""" from __future__ import annotations +import logging import random from datetime import datetime from datetime import UTC from pydantic import BaseModel from pydantic import Field +from pymongo.collection import Collection class Dataset(BaseModel): + """Database Dataset model.""" create_time: datetime = Field(default_factory=lambda: datetime.now(UTC)) train_names: list[str] test_names: list[str] @@ -53,4 +57,15 @@ class Dataset(BaseModel): test_names=test_name_list, validation_names=validation_name_list, ) + logging.debug('finished') return dataset + + def save( + self, + collection: Collection, + ) -> None: + """Save dataset to database.""" + res = collection.insert_one( + document=self.model_dump(), + ) + logging.debug('inserted document: %s', res) diff --git a/core/database/classes/exceptions.py b/core/database/classes/exceptions.py old mode 100644 new mode 100755 index 56d1194..5372031 --- a/core/database/classes/exceptions.py +++ b/core/database/classes/exceptions.py @@ -1,5 +1,6 @@ +"""Definition of database exception.""" from __future__ import annotations class NoDocumentFoundException(Exception): - pass + """Database exception for when no documents are found.""" diff --git a/core/database/classes/visual_communication.py b/core/database/classes/visual_communication.py old mode 100644 new mode 100755 index 2f6b44b..78e9e61 --- a/core/database/classes/visual_communication.py +++ b/core/database/classes/visual_communication.py @@ -1,3 +1,4 @@ +"""Definition of VisualCommunication model.""" from __future__ import annotations import logging @@ -15,12 +16,15 @@ from core.dto import ModelData class VisualCommunication(BaseModel): + """Visual communication model.""" + name: str image: Image.Image annotation: ModelData | None = None prediction: ModelData | None = None class Config: + """BaseModel configuration.""" arbitrary_types_allowed = True @classmethod @@ -38,12 +42,14 @@ class VisualCommunication(BaseModel): @classmethod def decode_image(cls, content: str) -> Image.Image: - """Decode image.""" + """Extract image from webencoded content.""" _, content_data = content.split(',') return Image.open(BytesIO(b64decode(content_data))) @field_serializer('image') - def serialize_image(image: Image.Image) -> bytes: # type: ignore + @classmethod + def serialize_image(cls, image: Image.Image) -> bytes: # type: ignore + """Convert image to bytes for storage in database.""" buffer = BytesIO() image.save(buffer, format='JPEG') return buffer.getvalue() @@ -54,6 +60,7 @@ class VisualCommunication(BaseModel): cls, image: Image.Image | BytesIO | bytes, ) -> Image.Image: + """Convert bytes input from database into image.""" if isinstance(image, bytes): image = BytesIO(image) if isinstance(image, BytesIO): From 2a85997aa7d2a7121df04898abba81dc539f5251 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:10:41 +0200 Subject: [PATCH 35/47] deleted unused file --- core/database/base.py | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 core/database/base.py diff --git a/core/database/base.py b/core/database/base.py deleted file mode 100644 index 3d73964..0000000 --- a/core/database/base.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -import logging -import os - -from dotenv import load_dotenv -from pymongo import MongoClient - - -def connect(): - """Connect to MongoDB.""" - # load env vars - load_dotenv() - necessary_env_vars = [ - 'MONGO_HOST', - 'MONGO_DB', - 'MONGO_COLLECTION', - ] - for env_var in necessary_env_vars: - assert env_var in os.environ, f"{env_var} not found" - # connect to database - client = MongoClient(os.getenv('MONGO_HOST')) - db = client[os.getenv('MONGO_DB')] - collection = db[os.getenv('MONGO_COLLECTION')] - collection.create_index('name', unique=True) - logging.info('connected to database') - return collection, db, client From a49e55c1592ba18cc4356c43dd1345f251a5d811 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:17:15 +0200 Subject: [PATCH 36/47] updating exposed content --- core/database/__init__.py | 9 ++------- core/database/utils/__init__.py | 8 +------- 2 files changed, 3 insertions(+), 14 deletions(-) mode change 100644 => 100755 core/database/utils/__init__.py diff --git a/core/database/__init__.py b/core/database/__init__.py index 238806b..f044a35 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -1,11 +1,6 @@ +"""Database module content.""" from __future__ import annotations -from .base import connect +from .classes import Dataset from .classes import NoDocumentFoundException from .classes import VisualCommunication -from .utils import count_documents -from .utils import get_visual_communication -from .utils import list_names -from .utils import upsert_annotations -from .utils import upsert_predictions -from .utils import upsert_visual_communication diff --git a/core/database/utils/__init__.py b/core/database/utils/__init__.py old mode 100644 new mode 100755 index b22e0e6..2891cda --- a/core/database/utils/__init__.py +++ b/core/database/utils/__init__.py @@ -1,8 +1,2 @@ +"""Database utils module content.""" from __future__ import annotations - -from .count_documents import count_documents -from .get_visual_communication import get_visual_communication -from .list_names import list_names -from .upsert_annotation import upsert_annotations -from .upsert_prediction import upsert_predictions -from .upsert_visual_communication import upsert_visual_communication From e09188453e456e84c2468eda96f40c3516bd2919 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:17:59 +0200 Subject: [PATCH 37/47] defined database connect function --- core/database/utils/connect.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 core/database/utils/connect.py diff --git a/core/database/utils/connect.py b/core/database/utils/connect.py new file mode 100644 index 0000000..98d4386 --- /dev/null +++ b/core/database/utils/connect.py @@ -0,0 +1,33 @@ +""" +Definition of function to connect to database +using environment variables. +""" +from __future__ import annotations + +import logging +import os + +from dotenv import load_dotenv +from pymongo import MongoClient + + +def connect(): + """Connect to MongoDB using env vars.""" + # load env vars + load_dotenv() + necessary_env_vars = [ + 'MONGO_HOST', + 'MONGO_DB', + 'MONGO_COLLECTION', + ] + for env_var in necessary_env_vars: + assert env_var in os.environ, f"{env_var} not found" + # connect to database + client = MongoClient(os.getenv('MONGO_HOST')) + db = client[os.getenv('MONGO_DB')] + # extract collection + collection = db[os.getenv('MONGO_COLLECTION')] + # set unique index on "name" + collection.create_index('name', unique=True) + logging.debug('finished') + return collection, db, client From eb4185c12949289398ee8977bcfaaeeec4031e0a Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:19:31 +0200 Subject: [PATCH 38/47] added function to count documents --- core/database/utils/__init__.py | 2 ++ core/database/utils/count_documents.py | 1 + 2 files changed, 3 insertions(+) mode change 100644 => 100755 core/database/utils/count_documents.py diff --git a/core/database/utils/__init__.py b/core/database/utils/__init__.py index 2891cda..b75aff4 100755 --- a/core/database/utils/__init__.py +++ b/core/database/utils/__init__.py @@ -1,2 +1,4 @@ """Database utils module content.""" from __future__ import annotations + +from .connect import connect diff --git a/core/database/utils/count_documents.py b/core/database/utils/count_documents.py old mode 100644 new mode 100755 index 1f74278..b254aef --- a/core/database/utils/count_documents.py +++ b/core/database/utils/count_documents.py @@ -1,3 +1,4 @@ +"""Definition of function to count documents in database.""" from __future__ import annotations from pymongo.collection import Collection From bb95e78132fa9b9cfe0fb76496bf6844ccac4315 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:20:03 +0200 Subject: [PATCH 39/47] renamed argument for clarity --- core/database/utils/count_documents.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/database/utils/count_documents.py b/core/database/utils/count_documents.py index b254aef..53a4a1c 100755 --- a/core/database/utils/count_documents.py +++ b/core/database/utils/count_documents.py @@ -6,16 +6,16 @@ from pymongo.collection import Collection def count_documents( collection: Collection, - has_annotation: bool = False, + only_with_annotation: bool = False, ) -> int: """ Get the total number of documents in database that matches the filters. """ assert isinstance(collection, Collection) - assert isinstance(has_annotation, bool) + assert isinstance(only_with_annotation, bool) # build query query = {} - if has_annotation: + if only_with_annotation: query['annotation'] = {'$ne': None} return collection.count_documents(filter=query) From 75b57bd28f0795b2f85cff19b1a8ed33cf3cc7a2 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:24:49 +0200 Subject: [PATCH 40/47] updated function to get visual communication --- .../utils/get_visual_communication.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) mode change 100644 => 100755 core/database/utils/get_visual_communication.py diff --git a/core/database/utils/get_visual_communication.py b/core/database/utils/get_visual_communication.py old mode 100644 new mode 100755 index 0add874..9b0b043 --- a/core/database/utils/get_visual_communication.py +++ b/core/database/utils/get_visual_communication.py @@ -1,3 +1,4 @@ +"""Definition of function to get visual communication from database.""" from __future__ import annotations import logging @@ -18,19 +19,21 @@ def get_visual_communication( query['annotation'] = {'$ne': None} else: query['annotation'] = {'$eq': None} - data = collection.aggregate([ - { - '$match': query, # find using filters - }, - { - '$sample': { - 'size': 1, # get one random + data = collection.aggregate( + pipeline=[ + { + '$match': query, # find using filters }, - }, - ]) + { + '$sample': { + 'size': 1, # get one random + }, + }, + ], + ) data_list = list(data) # read data from cursor object if len(data_list) == 0: - logging.error('failed getting visual communication') raise NoDocumentFoundException() - logging.info('finished') - return VisualCommunication.model_validate(data_list[0]) + vis_com = VisualCommunication.model_validate(data_list[0]) + logging.debug('finished') + return vis_com From c3aba7831b1d03bfc3baf4fbd367fef39a58c7a4 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Sun, 31 Mar 2024 12:26:51 +0200 Subject: [PATCH 41/47] updated function to list names of all vis com docs in database --- core/database/utils/list_names.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) mode change 100644 => 100755 core/database/utils/list_names.py diff --git a/core/database/utils/list_names.py b/core/database/utils/list_names.py old mode 100644 new mode 100755 index e9cd624..6184166 --- a/core/database/utils/list_names.py +++ b/core/database/utils/list_names.py @@ -1,3 +1,7 @@ +""" +Definition of function to list names +of all visual communication documents in database. +""" from __future__ import annotations from pymongo.collection import Collection @@ -5,14 +9,14 @@ from pymongo.collection import Collection def list_names( collection: Collection, - has_annotation: bool = True, + only_with_annotation: bool = True, ) -> list[str]: """List the names of entries that match the filters.""" assert isinstance(collection, Collection) - assert isinstance(has_annotation, bool) + assert isinstance(only_with_annotation, bool) # build query query = {} - if has_annotation: + if only_with_annotation: query['annotation'] = {'$ne': None} # execute query res_list = collection.find( From 0a3a07da1292def3b700d3212876e05a99e334cc Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 20:04:14 +0200 Subject: [PATCH 42/47] fixed column without title-formatting --- src/web/layout/labels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/layout/labels.py b/src/web/layout/labels.py index b7081d7..800b498 100644 --- a/src/web/layout/labels.py +++ b/src/web/layout/labels.py @@ -117,7 +117,7 @@ for title, options in textual_map.items(): id_dict = {'type': 'annotation', 'index': title.replace('_', '-')} textual_container.children.append( dmc.Container([ - html.B(title), + html.B(title.title()), dcc.RadioItems( options=options, id=id_dict, From f983145dfbb48e846a4a569cdec18388e84acafd Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 20:58:55 +0200 Subject: [PATCH 43/47] added database util functions --- core/database/__init__.py | 7 +++++++ core/database/utils/upsert_annotation.py | 2 +- core/database/utils/upsert_prediction.py | 2 +- core/database/utils/upsert_visual_communication.py | 0 4 files changed, 9 insertions(+), 2 deletions(-) mode change 100644 => 100755 core/database/utils/upsert_annotation.py mode change 100644 => 100755 core/database/utils/upsert_prediction.py mode change 100644 => 100755 core/database/utils/upsert_visual_communication.py diff --git a/core/database/__init__.py b/core/database/__init__.py index f044a35..ef9599c 100644 --- a/core/database/__init__.py +++ b/core/database/__init__.py @@ -4,3 +4,10 @@ from __future__ import annotations from .classes import Dataset from .classes import NoDocumentFoundException from .classes import VisualCommunication +from .utils import connect +from .utils.count_documents import count_documents +from .utils.get_visual_communication import get_visual_communication +from .utils.list_names import list_names +from .utils.upsert_annotation import upsert_annotation +from .utils.upsert_prediction import upsert_prediction +from .utils.upsert_visual_communication import upsert_visual_communication diff --git a/core/database/utils/upsert_annotation.py b/core/database/utils/upsert_annotation.py old mode 100644 new mode 100755 index 54be9eb..2124d2f --- a/core/database/utils/upsert_annotation.py +++ b/core/database/utils/upsert_annotation.py @@ -7,7 +7,7 @@ from pymongo.collection import Collection from core.dto import ModelData -def upsert_annotations( +def upsert_annotation( collection: Collection, vis_com_name: str, annotations: ModelData, diff --git a/core/database/utils/upsert_prediction.py b/core/database/utils/upsert_prediction.py old mode 100644 new mode 100755 index 2b1cd1a..f47d75b --- a/core/database/utils/upsert_prediction.py +++ b/core/database/utils/upsert_prediction.py @@ -7,7 +7,7 @@ from pymongo.collection import Collection from core.dto import ModelData -def upsert_predictions( +def upsert_prediction( collection: Collection, vis_com_name: str, predictions: ModelData, diff --git a/core/database/utils/upsert_visual_communication.py b/core/database/utils/upsert_visual_communication.py old mode 100644 new mode 100755 From 2ff51242393f089a8d5c09f95fdfdebf7b6a188c Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 21:00:08 +0200 Subject: [PATCH 44/47] deleted unused file --- core/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__init__.py diff --git a/core/__init__.py b/core/__init__.py deleted file mode 100644 index e69de29..0000000 From 12d043b22055984be1978bc2806877e5cabb65e3 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 21:00:55 +0200 Subject: [PATCH 45/47] updated to use new data classes and bugfixes --- src/web/layout/labels.py | 71 ++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/src/web/layout/labels.py b/src/web/layout/labels.py index 800b498..510b70f 100644 --- a/src/web/layout/labels.py +++ b/src/web/layout/labels.py @@ -4,35 +4,24 @@ import dash_mantine_components as dmc from dash import dcc from dash import html -from src.model_experiential import VisualSyntaxModelOutput -from src.model_interpersonal import AngleModelOutput -from src.model_interpersonal import ContactModelOutput -from src.model_interpersonal import DistanceModelOutput -from src.model_interpersonal import ModalityColorModelOutput -from src.model_interpersonal import ModalityDepthModelOutput -from src.model_interpersonal import ModalityLightingModelOutput -from src.model_interpersonal import PointOfViewModelOutput -from src.model_textual import FramingModelOutput -from src.model_textual import InformationValueModelOutput -from src.model_textual import SalienceModelOutput - - -def generate_option_labels(model) -> list[str]: - """Generate presentable list of attributes from an OutputModel.""" - labels = [ - label.replace('_', ' ').title() - for label in model.list_fields() - ] - return labels +from core.dto import AngleData +from core.dto import ContactData +from core.dto import DistanceData +from core.dto import FramingData +from core.dto import InformationValueData +from core.dto import ModalityColorData +from core.dto import ModalityDepthData +from core.dto import ModalityLightingData +from core.dto import PointOfViewData +from core.dto import SalienceData +from core.dto import VisualSyntaxData def generate_visual_syntax_options_map(): """Generate map of titles and options for visual syntax labels.""" options_map = {} # add experiential labels - options_map['visual syntax'] = generate_option_labels( - VisualSyntaxModelOutput, - ) + options_map['visual syntax'] = VisualSyntaxData.list_fields() return options_map @@ -40,21 +29,13 @@ def generate_interpersonal_options_map(): """Generate map of titles and options for interpersonal labels.""" options_map = {} # add interpersonal labels - options_map['contact'] = generate_option_labels(ContactModelOutput) - options_map['angle'] = generate_option_labels(AngleModelOutput) - options_map['point of view'] = generate_option_labels( - PointOfViewModelOutput, - ) - options_map['distance'] = generate_option_labels(DistanceModelOutput) - options_map['modality lighting'] = generate_option_labels( - ModalityLightingModelOutput, - ) - options_map['modality color'] = generate_option_labels( - ModalityColorModelOutput, - ) - options_map['modality depth'] = generate_option_labels( - ModalityDepthModelOutput, - ) + options_map['contact'] = ContactData.list_fields() + options_map['angle'] = AngleData.list_fields() + options_map['point of view'] = PointOfViewData.list_fields() + options_map['distance'] = DistanceData.list_fields() + options_map['modality lighting'] = ModalityLightingData.list_fields() + options_map['modality color'] = ModalityColorData.list_fields() + options_map['modality depth'] = ModalityDepthData.list_fields() return options_map @@ -62,11 +43,9 @@ def generate_textual_options_map(): """Generate map of titles and options for textual labels.""" options_map = {} # add textual labels - options_map['information value'] = generate_option_labels( - InformationValueModelOutput, - ) - options_map['framing'] = generate_option_labels(FramingModelOutput) - options_map['salience'] = generate_option_labels(SalienceModelOutput) + options_map['information value'] = InformationValueData.list_fields() + options_map['framing'] = FramingData.list_fields() + options_map['salience'] = SalienceData.list_fields() return options_map @@ -83,7 +62,7 @@ for title, options in experiential_map.items(): dmc.Container([ html.B(title.title()), dcc.RadioItems( - options=options, + options=[text.replace('_', ' ') for text in options], id=id_dict, ), ]), @@ -101,7 +80,7 @@ for title, options in interpersonal_map.items(): dmc.Container([ html.B(title.title()), dcc.RadioItems( - options=options, + options=[text.replace('_', ' ') for text in options], id=id_dict, ), ]), @@ -119,7 +98,7 @@ for title, options in textual_map.items(): dmc.Container([ html.B(title.title()), dcc.RadioItems( - options=options, + options=[text.replace('_', ' ') for text in options], id=id_dict, ), ]), From 1975b131516c2fe114a12373d759dbf94ac284f3 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 21:01:36 +0200 Subject: [PATCH 46/47] updated to use new data classes and bugfixes --- src/web/app.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/web/app.py b/src/web/app.py index 4f2d4f9..151fcd9 100644 --- a/src/web/app.py +++ b/src/web/app.py @@ -13,15 +13,14 @@ from dash_auth import BasicAuth from pydantic import ValidationError from .layout import app_layout -from src.database import connect -from src.database import get_visual_communication -from src.database import ModelOutputs -from src.database import NoDocumentFoundException -from src.database import total_annotated -from src.database import total_documents -from src.database import upsert_annotations -from src.database import upsert_visual_communication -from src.database import VisualCommunication +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]) @@ -82,8 +81,14 @@ def update_progress_bar( logging.info('began updating progress bar') global collection # get values from database - num_total = total_documents(collection=collection) - num_handled = total_annotated(collection=collection) + 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 @@ -200,9 +205,9 @@ def cycle_visual_communication_data( in zip(annotation_keys, annotation_values) } # instantiate ModelOutputs object - annotations = ModelOutputs.from_annotations(**annotation_map) + annotations = ModelData.from_annotations(**annotation_map) # save data to database - upsert_annotations( + upsert_annotation( collection=collection, vis_com_name=vis_com_name, annotations=annotations, From df2610dc2274a983ea8aedde53cc2c5343cdd4f3 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Wed, 3 Apr 2024 21:02:18 +0200 Subject: [PATCH 47/47] added copying in core code --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5671d4d..d481a47 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,8 +57,9 @@ RUN mkdir -p /home/app && \ # add code while changing ownership WORKDIR $APP_HOME COPY --chown=app:app ./src ./src +COPY --chown=app:app ./core ./core # change to the app user USER app -ENTRYPOINT [ "gunicorn", "src.main:server", "-b", "0.0.0.0:8050" ] \ No newline at end of file +ENTRYPOINT [ "gunicorn", "src.main:server", "-b", "0.0.0.0:8050" ]