Compare commits

..
5 Commits
Author SHA1 Message Date
Brian Bjarke Jensen eaca23f27a updated to use new class
CI Pipeline / Test (pull_request) Successful in 2m7s
CI Pipeline / Build and Publish (./Dockerfile.model, ${{ vars.docker_repo_url }}/${{ gitea.repository }}/model) (pull_request) Successful in 6m26s
CI Pipeline / Build and Publish (./Dockerfile.web_ui, ${{ vars.docker_repo_url }}/${{ gitea.repository }}/web_ui) (pull_request) Successful in 5m40s
2024-06-15 23:18:27 +02:00
Brian Bjarke Jensen 0bd4008e72 updated to use new class 2024-06-15 23:17:28 +02:00
Brian Bjarke Jensen 123cab2500 updated to use new functions 2024-06-15 23:16:42 +02:00
Brian Bjarke Jensen d09cb9dd20 added delete function 2024-06-15 23:15:31 +02:00
Brian Bjarke Jensen e1075b3551 ignore outdated class reference 2024-06-15 23:14:55 +02:00
9 changed files with 200 additions and 81 deletions
+1 -1
View File
@@ -98,7 +98,7 @@ if __name__ == '__main__':
try: try:
# save image to buffer # save image to buffer
buffer = BytesIO() buffer = BytesIO()
vis_com.image.save(buffer, 'png') vis_com.image.save(buffer, 'png') # type: ignore
# put buffer in minio # put buffer in minio
object_name = put( object_name = put(
client=minio_client, client=minio_client,
+1
View File
@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
from .connect import connect from .connect import connect
from .delete import delete
from .get import get from .get import get
from .put import put from .put import put
+29
View File
@@ -0,0 +1,29 @@
"""Definition of delete function."""
from __future__ import annotations
import logging
import os
from minio import Minio
def delete(
client: Minio,
object_name: str,
) -> None:
"""Delete object from MinIO."""
assert isinstance(client, Minio)
assert isinstance(object_name, str)
bucket_name = os.getenv('MINIO_BUCKET_NAME', default=None)
assert isinstance(bucket_name, str)
# remove object
try:
client.remove_object(
bucket_name=bucket_name,
object_name=object_name,
)
except Exception as exc:
logging.debug(exc)
logging.error('failed deleting %s', object_name)
else:
logging.debug('deleted %s', object_name)
@@ -10,6 +10,7 @@ from pathlib import Path
from minio import Minio from minio import Minio
from PIL import Image from PIL import Image
from pydantic import BaseModel from pydantic import BaseModel
from pymongo.collection import Collection
from shared.data_store import get from shared.data_store import get
from shared.data_store import put from shared.data_store import put
@@ -34,23 +35,60 @@ class VisualCommunication(BaseModel):
return cls.__name__ return cls.__name__
@classmethod @classmethod
def from_file(cls, path: Path, minio_client: Minio) -> VisualCommunication: def upload_image_to_minio(
"""Instantiate from file.""" cls,
assert isinstance(path, Path) image: Image.Image,
minio_client: Minio,
) -> str:
"""Upload image to MinIO and return MD5 checksum of hashed image."""
assert isinstance(image, Image.Image)
assert isinstance(minio_client, Minio) assert isinstance(minio_client, Minio)
# determine name
name = path.stem
# open and upload image to minio
image = Image.open(path)
image.load()
buffer = BytesIO() buffer = BytesIO()
image.save(buffer, 'png') image.save(buffer, 'png')
object_name = put( object_name = put(
client=minio_client, client=minio_client,
buffer=buffer, buffer=buffer,
) )
return object_name
@classmethod
def from_name_and_image(
cls,
name: str,
image: Image.Image,
minio_client: Minio,
) -> VisualCommunication:
"""
Instantiate from filename and image
that is automatically uploaded to MinIO.
"""
assert isinstance(name, str)
assert isinstance(image, Image.Image)
assert isinstance(minio_client, Minio)
# upload file to minio
object_name = VisualCommunication.upload_image_to_minio(
image=image,
minio_client=minio_client,
)
return VisualCommunication(name=name, object_name=object_name) return VisualCommunication(name=name, object_name=object_name)
@classmethod
def from_file(cls, path: Path, minio_client: Minio) -> VisualCommunication:
"""Instantiate from file."""
assert isinstance(path, Path)
assert isinstance(minio_client, Minio)
# determine name
name = path.stem
# open image
image = Image.open(path)
image.load()
# instantiate object
return VisualCommunication.from_name_and_image(
name=name,
image=image,
minio_client=minio_client,
)
@classmethod @classmethod
def decode_image(cls, content: str) -> Image.Image: def decode_image(cls, content: str) -> Image.Image:
"""Extract image from webencoded content.""" """Extract image from webencoded content."""
@@ -69,6 +107,13 @@ class VisualCommunication(BaseModel):
im = Image.open(buffer) im = Image.open(buffer)
return im return im
def save_to_mongo(self, collection: Collection) -> None:
"""Save self as document in MongoDB."""
res = collection.insert_one(
document=self.model_dump(),
)
assert res.acknowledged
def webencoded_image(self, minio_client: Minio) -> str: def webencoded_image(self, minio_client: Minio) -> str:
"""Convert image to be displayed on webpage.""" """Convert image to be displayed on webpage."""
assert isinstance(minio_client, Minio) assert isinstance(minio_client, Minio)
+19 -2
View File
@@ -2,10 +2,27 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from shared.database.classes import VisualCommunication from dotenv import load_dotenv
from shared.data_store import connect as connect_minio
from shared.database import connect as connect_mongo
from shared.database.classes import VisualCommunication
from shared.utils import check_env
from shared.utils import setup_logging
if __name__ == '__main__': if __name__ == '__main__':
# load in env file
env_path = Path(__file__).parent.parent / 'server.env'
assert env_path.exists()
load_dotenv(env_path)
# ensure env vars set
check_env()
# setup logging
setup_logging()
# connect to minIO
minio_client = connect_minio()
# connect to MongoDB
collection, db, client = connect_mongo()
# get list of image paths # get list of image paths
test_dir = Path(__file__).parent test_dir = Path(__file__).parent
img_dir = test_dir / 'imgs' img_dir = test_dir / 'imgs'
@@ -13,7 +30,7 @@ if __name__ == '__main__':
print(img_path_list) print(img_path_list)
# instantiate data object # instantiate data object
vis_com_list = [ vis_com_list = [
VisualCommunication.from_file(path) VisualCommunication.from_file(path, minio_client=minio_client)
for path for path
in img_path_list in img_path_list
] ]
+17 -11
View File
@@ -1,15 +1,29 @@
from __future__ import annotations from __future__ import annotations
import os
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv from dotenv import load_dotenv
from pymongo.errors import DuplicateKeyError from pymongo.errors import DuplicateKeyError
from shared.database import connect from shared.data_store import connect as connect_minio
from shared.database import connect as connect_mongo
from shared.database.classes import VisualCommunication from shared.database.classes import VisualCommunication
from shared.utils import check_env
from shared.utils import setup_logging
if __name__ == '__main__': if __name__ == '__main__':
# load in env file
env_path = Path(__file__).parent.parent / 'server.env'
assert env_path.exists()
load_dotenv(env_path)
# ensure env vars set
check_env()
# setup logging
setup_logging()
# connect to minIO
minio_client = connect_minio()
# connect to MongoDB
collection, db, client = connect_mongo()
# get list of image paths # get list of image paths
test_dir = Path(__file__).parent test_dir = Path(__file__).parent
img_dir = test_dir / 'imgs' img_dir = test_dir / 'imgs'
@@ -17,20 +31,12 @@ if __name__ == '__main__':
print(img_path_list) print(img_path_list)
# instantiate data object # instantiate data object
vis_com_list = [ vis_com_list = [
VisualCommunication.from_file(path) VisualCommunication.from_file(path, minio_client=minio_client)
for path for path
in img_path_list in img_path_list
] ]
for vis_com in vis_com_list: for vis_com in vis_com_list:
print(repr(vis_com)) print(repr(vis_com))
# prepare env vars
env_path = test_dir.parent / 'local.env'
assert env_path.exists()
load_dotenv(env_path)
os.environ['MONGO_HOST'] = 'localhost'
# connect to database
collection, db, client = connect()
print(client.server_info())
# upload images # upload images
for vis_com in vis_com_list: for vis_com in vis_com_list:
try: try:
+17 -10
View File
@@ -5,10 +5,25 @@ from pathlib import Path
from dotenv import load_dotenv from dotenv import load_dotenv
from pymongo.errors import DuplicateKeyError from pymongo.errors import DuplicateKeyError
from shared.database import connect from shared.data_store import connect as connect_minio
from shared.database import connect as connect_mongo
from shared.database import VisualCommunication from shared.database import VisualCommunication
from shared.utils import check_env
from shared.utils import setup_logging
if __name__ == '__main__': if __name__ == '__main__':
# load in env file
env_path = Path(__file__).parent.parent / 'server.env'
assert env_path.exists()
load_dotenv(env_path)
# ensure env vars set
check_env()
# setup logging
setup_logging()
# connect to minIO
minio_client = connect_minio()
# connect to MongoDB
collection, db, client = connect_mongo()
# get list of image paths # get list of image paths
ext_img_dir = Path('/Volumes/BW-PSSD/Mixed Methods/') ext_img_dir = Path('/Volumes/BW-PSSD/Mixed Methods/')
assert ext_img_dir.exists() assert ext_img_dir.exists()
@@ -20,19 +35,11 @@ if __name__ == '__main__':
print(f"found {len(img_path_list)} images") print(f"found {len(img_path_list)} images")
# create visual communication objects # create visual communication objects
vis_com_list = [ vis_com_list = [
VisualCommunication.from_file(path) VisualCommunication.from_file(path, minio_client=minio_client)
for path for path
in img_path_list in img_path_list
] ]
print(f"created {len(vis_com_list)} visual communication objects") print(f"created {len(vis_com_list)} visual communication objects")
# prepare env vars
env_path = Path(__file__).parent.parent / 'server.env'
assert env_path.exists()
load_dotenv(env_path)
# connect to database
collection, db, client = connect()
assert client.server_info() is not None
print('connected to database')
# upload images # upload images
for vis_com in vis_com_list: for vis_com in vis_com_list:
try: try:
+16 -20
View File
@@ -1,46 +1,42 @@
from __future__ import annotations from __future__ import annotations
import logging
import os
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv from dotenv import load_dotenv
from shared.database import connect from shared.data_store import connect as connect_minio
from shared.database import connect as connect_mongo
from shared.database import upsert_prediction from shared.database import upsert_prediction
from shared.database.classes import VisualCommunication from shared.database.classes import VisualCommunication
from shared.utils import check_env
from shared.utils import setup_logging
if __name__ == '__main__': if __name__ == '__main__':
# load in env file
env_path = Path(__file__).parent.parent / 'server.env'
assert env_path.exists()
load_dotenv(env_path)
# ensure env vars set
check_env()
# setup logging # setup logging
fmt = ( setup_logging()
'%(asctime)s | ' # connect to minIO
'%(levelname)s | ' minio_client = connect_minio()
'%(filename)s | ' # connect to MongoDB
'%(funcName)s | ' collection, db, client = connect_mongo()
'%(message)s'
)
datefmt = '%Y-%m-%d %H:%M:%S'
logging.basicConfig(format=fmt, datefmt=datefmt, level=logging.INFO)
# get list of image paths # get list of image paths
test_dir = Path(__file__).parent test_dir = Path(__file__).parent
img_dir = test_dir / 'imgs' img_dir = test_dir / 'imgs'
img_path_list = [path for path in img_dir.glob('*.jpeg') if path.is_file()] img_path_list = [path for path in img_dir.glob('*.jpeg') if path.is_file()]
# instantiate data object # instantiate data object
vis_com_list = [ vis_com_list = [
VisualCommunication.from_file(path) VisualCommunication.from_file(path, minio_client=minio_client)
for path for path
in img_path_list in img_path_list
] ]
# generate random predictions # generate random predictions
for vis_com in vis_com_list: for vis_com in vis_com_list:
vis_com.generate_random_prediction() vis_com.generate_random_prediction()
# prepare env vars
env_path = test_dir.parent / 'local.env'
assert env_path.exists()
load_dotenv(env_path)
os.environ['MONGO_HOST'] = 'localhost'
# connect to database
collection, db, client = connect()
# upload visual communication # upload visual communication
for vis_com in vis_com_list: for vis_com in vis_com_list:
if vis_com.prediction is None: if vis_com.prediction is None:
+36 -18
View File
@@ -16,11 +16,11 @@ from pydantic import ValidationError
from pymongo.collection import Collection from pymongo.collection import Collection
from .layout import app_layout from .layout import app_layout
from shared.data_store import delete as delete_from_minio
from shared.database import count_documents from shared.database import count_documents
from shared.database import get_visual_communication from shared.database import get_visual_communication
from shared.database import NoDocumentFoundException from shared.database import NoDocumentFoundException
from shared.database import upsert_annotation from shared.database import upsert_annotation
from shared.database import upsert_visual_communication
from shared.database import VisualCommunication from shared.database import VisualCommunication
from shared.dto import ModelData from shared.dto import ModelData
@@ -120,34 +120,52 @@ def init_app(
filename_list: list[str] | None, filename_list: list[str] | None,
) -> tuple[str | None, str | None]: ) -> tuple[str | None, str | None]:
"""Upload image to database through web ui.""" """Upload image to database through web ui."""
# stop early if possible try:
if content_list is None or filename_list is None: # stop if no input
logging.info('nothing to upload.') assert (
return None, 'nothing to upload'.title() content_list is not None
# build list of visual communication ) and (
vis_com_list = [] filename_list is not None
), 'nothing to upload'
# handle input
failed_filename_list = []
for content, filename in zip(content_list, filename_list): for content, filename in zip(content_list, filename_list):
try: try:
# decode image content
image = VisualCommunication.decode_image(content=content) image = VisualCommunication.decode_image(content=content)
vis_com = VisualCommunication( except Exception:
logging.debug('failed decoding %s', filename)
failed_filename_list.append(filename)
continue
try:
# instantiate to upload image to minio
vis_com = VisualCommunication.from_name_and_image(
name=filename, name=filename,
image=image, image=image,
minio_client=minio_client,
) )
except Exception as exc: except Exception as exc:
logging.error('failed handling data from file: %s', filename)
logging.debug(exc) logging.debug(exc)
failed_filename_list.append(filename)
continue continue
vis_com_list.append(vis_com) try:
# upsert documents # save to mongodb
success = upsert_visual_communication( vis_com.save_to_mongo(collection=mongo_collection)
collection=mongo_collection, except Exception as exc:
visual_communication_list=vis_com_list, logging.debug(exc)
failed_filename_list.append(filename)
# remove document from minio
delete_from_minio(
client=minio_client,
object_name=vis_com.object_name,
) )
if success: assert len(failed_filename_list) == 0, f"failed uploading:{
logging.info('uploaded %s files to database', len(vis_com_list)) '\n'.join(failed_filename_list)
}"
except Exception as exc:
logging.debug(exc)
return None, str(exc).title()
return 'successfully uploaded images'.title(), None return 'successfully uploaded images'.title(), None
logging.error('failed uploading images to database')
return None, 'failed uploading images'.title()
# define callback: cycle visual communication data # define callback: cycle visual communication data
@app.callback( @app.callback(