Merge pull request 'move_images_to_minio' (#44) from move_images_to_minio into main
Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
@@ -0,0 +1,122 @@
|
|||||||
|
"""Script to move all images from MongoDB to MinIO."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from pymongo.collection import Collection
|
||||||
|
|
||||||
|
from shared.data_store import connect as connect_minio
|
||||||
|
from shared.data_store import put
|
||||||
|
from shared.database import connect
|
||||||
|
from shared.database import VisualCommunication
|
||||||
|
from shared.utils import check_env
|
||||||
|
from shared.utils import setup_logging
|
||||||
|
|
||||||
|
|
||||||
|
def list_mongo_document_ids(
|
||||||
|
collection: Collection,
|
||||||
|
) -> list[ObjectId]:
|
||||||
|
"""Get list of all VisualCommunication documents in MongoDB."""
|
||||||
|
# prepare query
|
||||||
|
query = collection.find(
|
||||||
|
filter={},
|
||||||
|
projection={'_id': True},
|
||||||
|
)
|
||||||
|
# execute query
|
||||||
|
res_list = list(query)
|
||||||
|
logging.debug('got %s document(s)', len(res_list))
|
||||||
|
# convert result
|
||||||
|
id_list = [elem['_id'] for elem in res_list]
|
||||||
|
return id_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_visual_communication(
|
||||||
|
collection: Collection,
|
||||||
|
doc_id: ObjectId,
|
||||||
|
) -> VisualCommunication:
|
||||||
|
"""Get image from MongoDB."""
|
||||||
|
# prepare query
|
||||||
|
query = collection.find(
|
||||||
|
filter={'_id': doc_id},
|
||||||
|
projection={'_id': False},
|
||||||
|
)
|
||||||
|
# execute query
|
||||||
|
res_list = list(query)
|
||||||
|
logging.debug('got %s document(s)', len(res_list))
|
||||||
|
# convert result
|
||||||
|
vis_com = VisualCommunication.model_validate(res_list[0])
|
||||||
|
return vis_com
|
||||||
|
|
||||||
|
|
||||||
|
def update_visual_communication(
|
||||||
|
collection: Collection,
|
||||||
|
vis_com: VisualCommunication,
|
||||||
|
) -> None:
|
||||||
|
"""Update VisualCommunication document in MongoDB."""
|
||||||
|
try:
|
||||||
|
collection.update_one(
|
||||||
|
filter={
|
||||||
|
'name': vis_com.name,
|
||||||
|
},
|
||||||
|
update={
|
||||||
|
'$set': vis_com.model_dump(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.error('failed saving %s', vis_com.name)
|
||||||
|
logging.debug(exc)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
# list documents in mongoDB
|
||||||
|
id_list = list_mongo_document_ids(collection)
|
||||||
|
for doc_id in id_list:
|
||||||
|
try:
|
||||||
|
# get image from MongoDB
|
||||||
|
vis_com = get_visual_communication(collection, doc_id)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(exc)
|
||||||
|
logging.error('failed getting image from document: %s', doc_id)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
# save image to buffer
|
||||||
|
buffer = BytesIO()
|
||||||
|
vis_com.image.save(buffer, 'png') # type: ignore
|
||||||
|
# put buffer in minio
|
||||||
|
object_name = put(
|
||||||
|
client=minio_client,
|
||||||
|
buffer=buffer,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(exc)
|
||||||
|
logging.error('failed saving image to minio')
|
||||||
|
continue
|
||||||
|
# update visual communication in mongodb
|
||||||
|
try:
|
||||||
|
vis_com.image = None # type: ignore
|
||||||
|
vis_com.object_name = object_name
|
||||||
|
update_visual_communication(collection, vis_com)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(exc)
|
||||||
|
logging.error(
|
||||||
|
'failed updating visual communication %s', vis_com.name,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
logging.debug('updated visual communication %s', vis_com.name)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .connect import connect
|
||||||
|
from .delete import delete
|
||||||
|
from .get import get
|
||||||
|
from .put import put
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""Definition of connect function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
|
|
||||||
|
|
||||||
|
def connect(
|
||||||
|
) -> Minio:
|
||||||
|
"""Connect to MinIO server"""
|
||||||
|
# ensure necessary env vars available
|
||||||
|
necesasary_var_list = [
|
||||||
|
'MINIO_ENDPOINT',
|
||||||
|
'MINIO_ACCESS_KEY',
|
||||||
|
'MINIO_SECRET_KEY',
|
||||||
|
'MINIO_BUCKET_NAME',
|
||||||
|
]
|
||||||
|
for env_var in necesasary_var_list:
|
||||||
|
# ensure env var set
|
||||||
|
assert (
|
||||||
|
env_var in os.environ
|
||||||
|
), (
|
||||||
|
f"environment variable not set: {env_var}"
|
||||||
|
)
|
||||||
|
# prepare arguments
|
||||||
|
minio_endpoint = os.getenv('MINIO_ENDPOINT', default=None)
|
||||||
|
assert isinstance(minio_endpoint, str)
|
||||||
|
minio_access_key = os.getenv('MINIO_ACCESS_KEY', default=None)
|
||||||
|
assert isinstance(minio_access_key, str)
|
||||||
|
minio_secret_key = os.getenv('MINIO_SECRET_KEY', default=None)
|
||||||
|
assert isinstance(minio_secret_key, str)
|
||||||
|
minio_bucket_name = os.getenv('MINIO_BUCKET_NAME', default=None)
|
||||||
|
assert isinstance(minio_bucket_name, str)
|
||||||
|
# connect client
|
||||||
|
client = Minio(
|
||||||
|
endpoint=minio_endpoint,
|
||||||
|
access_key=minio_access_key,
|
||||||
|
secret_key=minio_secret_key,
|
||||||
|
secure=False,
|
||||||
|
)
|
||||||
|
# ensure bucket exists
|
||||||
|
if not client.bucket_exists(bucket_name=minio_bucket_name):
|
||||||
|
client.make_bucket(bucket_name=minio_bucket_name)
|
||||||
|
return client
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""Definition of get function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
|
|
||||||
|
|
||||||
|
def get(
|
||||||
|
client: Minio,
|
||||||
|
object_name: str,
|
||||||
|
) -> BytesIO:
|
||||||
|
"""Get buffer from bucket in MinIO."""
|
||||||
|
assert isinstance(client, Minio)
|
||||||
|
assert isinstance(object_name, str)
|
||||||
|
bucket_name = os.getenv('MINIO_BUCKET_NAME', default=None)
|
||||||
|
assert isinstance(bucket_name, str)
|
||||||
|
# get buffer
|
||||||
|
try:
|
||||||
|
response = client.get_object(bucket_name, object_name)
|
||||||
|
buffer = BytesIO(response.data)
|
||||||
|
finally:
|
||||||
|
response.close()
|
||||||
|
response.release_conn()
|
||||||
|
buffer.seek(0)
|
||||||
|
return buffer
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"""Definition of put function."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from hashlib import md5
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
|
|
||||||
|
|
||||||
|
def put(
|
||||||
|
client: Minio,
|
||||||
|
buffer: BytesIO,
|
||||||
|
) -> str:
|
||||||
|
"""Put buffer in bucket in MinIO and return MD5 checksum as object name."""
|
||||||
|
assert isinstance(client, Minio)
|
||||||
|
assert isinstance(buffer, BytesIO)
|
||||||
|
bucket_name = os.getenv('MINIO_BUCKET_NAME', default=None)
|
||||||
|
assert isinstance(bucket_name, str)
|
||||||
|
# get md5 of image
|
||||||
|
checksum = md5(buffer.getbuffer()).hexdigest()
|
||||||
|
# prepare for saving
|
||||||
|
num_bytes = buffer.tell()
|
||||||
|
buffer.seek(0)
|
||||||
|
# send data to bucket
|
||||||
|
try:
|
||||||
|
client.put_object(
|
||||||
|
bucket_name=bucket_name,
|
||||||
|
object_name=checksum,
|
||||||
|
length=num_bytes,
|
||||||
|
data=buffer,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.error('failed saving data to MinIO')
|
||||||
|
raise exc
|
||||||
|
logging.debug('saved data to %s', checksum)
|
||||||
|
return checksum
|
||||||
@@ -7,11 +7,13 @@ from base64 import b64encode
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from pydantic import field_serializer
|
from pymongo.collection import Collection
|
||||||
from pydantic import field_validator
|
|
||||||
|
|
||||||
|
from shared.data_store import get
|
||||||
|
from shared.data_store import put
|
||||||
from shared.dto import ModelData
|
from shared.dto import ModelData
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@ class VisualCommunication(BaseModel):
|
|||||||
"""Visual communication model."""
|
"""Visual communication model."""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
image: Image.Image
|
object_name: str
|
||||||
annotation: ModelData | None = None
|
annotation: ModelData | None = None
|
||||||
prediction: ModelData | None = None
|
prediction: ModelData | None = None
|
||||||
|
|
||||||
@@ -33,12 +35,59 @@ class VisualCommunication(BaseModel):
|
|||||||
return cls.__name__
|
return cls.__name__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, path: Path) -> VisualCommunication:
|
def upload_image_to_minio(
|
||||||
|
cls,
|
||||||
|
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)
|
||||||
|
buffer = BytesIO()
|
||||||
|
image.save(buffer, 'png')
|
||||||
|
object_name = put(
|
||||||
|
client=minio_client,
|
||||||
|
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)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_file(cls, path: Path, minio_client: Minio) -> VisualCommunication:
|
||||||
"""Instantiate from file."""
|
"""Instantiate from file."""
|
||||||
|
assert isinstance(path, Path)
|
||||||
|
assert isinstance(minio_client, Minio)
|
||||||
|
# determine name
|
||||||
name = path.stem
|
name = path.stem
|
||||||
|
# open image
|
||||||
image = Image.open(path)
|
image = Image.open(path)
|
||||||
image.load()
|
image.load()
|
||||||
return VisualCommunication(name=name, image=image)
|
# 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:
|
||||||
@@ -46,35 +95,33 @@ class VisualCommunication(BaseModel):
|
|||||||
_, content_data = content.split(',')
|
_, content_data = content.split(',')
|
||||||
return Image.open(BytesIO(b64decode(content_data)))
|
return Image.open(BytesIO(b64decode(content_data)))
|
||||||
|
|
||||||
@field_serializer('image')
|
def get_image(self, minio_client: Minio) -> Image.Image:
|
||||||
@classmethod
|
"""Load image data from minio."""
|
||||||
def serialize_image(cls, image: Image.Image) -> bytes: # type: ignore
|
assert isinstance(minio_client, Minio)
|
||||||
"""Convert image to bytes for storage in database."""
|
# get buffer from minio
|
||||||
buffer = BytesIO()
|
buffer = get(
|
||||||
image.save(buffer, format='JPEG')
|
client=minio_client,
|
||||||
return buffer.getvalue()
|
object_name=self.object_name,
|
||||||
|
)
|
||||||
|
# convert data to image
|
||||||
|
im = Image.open(buffer)
|
||||||
|
return im
|
||||||
|
|
||||||
@field_validator('image', mode='before')
|
def save_to_mongo(self, collection: Collection) -> None:
|
||||||
@classmethod
|
"""Save self as document in MongoDB."""
|
||||||
def convert_to_image(
|
res = collection.insert_one(
|
||||||
cls,
|
document=self.model_dump(),
|
||||||
image: Image.Image | BytesIO | bytes,
|
)
|
||||||
) -> Image.Image:
|
assert res.acknowledged
|
||||||
"""Convert bytes input from database into image."""
|
|
||||||
if isinstance(image, bytes):
|
|
||||||
image = BytesIO(image)
|
|
||||||
if isinstance(image, BytesIO):
|
|
||||||
image = Image.open(image)
|
|
||||||
return image
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def webencoded_image(self, minio_client: Minio) -> str:
|
||||||
return f"{self.classname()}(name='{self.name}')"
|
|
||||||
|
|
||||||
def webencoded_image(self) -> str:
|
|
||||||
"""Convert image to be displayed on webpage."""
|
"""Convert image to be displayed on webpage."""
|
||||||
|
assert isinstance(minio_client, Minio)
|
||||||
|
# get image from minio
|
||||||
|
image = self.get_image(minio_client)
|
||||||
# convert images to bytes string
|
# convert images to bytes string
|
||||||
buffer = BytesIO()
|
buffer = BytesIO()
|
||||||
self.image.save(buffer, format='png')
|
image.save(buffer, format='png')
|
||||||
img_enc = b64encode(buffer.getvalue()).decode('utf-8')
|
img_enc = b64encode(buffer.getvalue()).decode('utf-8')
|
||||||
return f"data:image/png;base64, {img_enc}"
|
return f"data:image/png;base64, {img_enc}"
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ def check_env() -> None:
|
|||||||
'MONGO_PASSWORD',
|
'MONGO_PASSWORD',
|
||||||
'DASH_AUTH_USERNAME',
|
'DASH_AUTH_USERNAME',
|
||||||
'DASH_AUTH_PASSWORD',
|
'DASH_AUTH_PASSWORD',
|
||||||
|
'MINIO_ENDPOINT',
|
||||||
|
'MINIO_ACCESS_KEY',
|
||||||
|
'MINIO_SECRET_KEY',
|
||||||
|
'MINIO_BUCKET_NAME',
|
||||||
}
|
}
|
||||||
for env_var in necesasary_var_list:
|
for env_var in necesasary_var_list:
|
||||||
# ensure env var set
|
# ensure env var set
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ def setup_logging() -> None:
|
|||||||
fmt = (
|
fmt = (
|
||||||
'%(asctime)s | '
|
'%(asctime)s | '
|
||||||
'%(levelname)s | '
|
'%(levelname)s | '
|
||||||
'%(filename)s | '
|
'%(name)s | '
|
||||||
'%(funcName)s | '
|
'%(funcName)s | '
|
||||||
'%(message)s'
|
'%(message)s'
|
||||||
)
|
)
|
||||||
datefmt = '%Y-%m-%d %H:%M:%S'
|
datefmt = '%Y-%m-%d %H:%M:%S'
|
||||||
logging.basicConfig(format=fmt, datefmt=datefmt, level=level)
|
logging.basicConfig(format=fmt, datefmt=datefmt, level=level)
|
||||||
|
# change levels for modules that spam the log
|
||||||
|
logging.getLogger('pymongo').setLevel(logging.WARNING)
|
||||||
|
logging.getLogger('urllib3').setLevel(logging.INFO)
|
||||||
|
logging.getLogger('PIL').setLevel(logging.INFO)
|
||||||
logging.debug('finished')
|
logging.debug('finished')
|
||||||
|
|||||||
@@ -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
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
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 shared.data_store import (
|
||||||
|
connect as connect_minio,
|
||||||
|
)
|
||||||
from shared.database import connect
|
from shared.database import connect
|
||||||
from shared.database import get_visual_communication
|
from shared.database import get_visual_communication
|
||||||
|
from shared.utils import check_env
|
||||||
|
from shared.utils import setup_logging
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# prepare env vars
|
# load in env file
|
||||||
env_path = Path(__file__).parent.parent / 'local.env'
|
env_path = Path(__file__).parent.parent / 'server.env'
|
||||||
assert env_path.exists()
|
assert env_path.exists()
|
||||||
load_dotenv(env_path)
|
load_dotenv(env_path)
|
||||||
os.environ['MONGO_HOST'] = 'localhost'
|
# ensure env vars set
|
||||||
# connect to database
|
check_env()
|
||||||
|
# setup logging
|
||||||
|
setup_logging()
|
||||||
|
# connect to minIO
|
||||||
|
minio_client = connect_minio()
|
||||||
|
# connect to MongoDB
|
||||||
collection, db, client = connect()
|
collection, db, client = connect()
|
||||||
print(client.server_info())
|
|
||||||
# get visual communication
|
# get visual communication
|
||||||
vis_com = get_visual_communication(collection)
|
vis_com = get_visual_communication(collection)
|
||||||
print(vis_com)
|
print(repr(vis_com))
|
||||||
|
image = vis_com.get_image(minio_client=minio_client)
|
||||||
|
image.show()
|
||||||
|
|||||||
+17
-11
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
+55
-35
@@ -11,21 +11,23 @@ from dash import Input
|
|||||||
from dash import Output
|
from dash import Output
|
||||||
from dash import State
|
from dash import State
|
||||||
from dash_auth import BasicAuth
|
from dash_auth import BasicAuth
|
||||||
|
from minio import Minio
|
||||||
from pydantic import ValidationError
|
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
|
||||||
|
|
||||||
|
|
||||||
def init_app(
|
def init_app(
|
||||||
collection: Collection,
|
mongo_collection: Collection,
|
||||||
|
minio_client: Minio,
|
||||||
) -> Dash:
|
) -> Dash:
|
||||||
"""Initialise web UI application."""
|
"""Initialise web UI application."""
|
||||||
# setup app
|
# setup app
|
||||||
@@ -92,11 +94,11 @@ def init_app(
|
|||||||
assert isinstance(filename_list, list)
|
assert isinstance(filename_list, list)
|
||||||
# get values from database
|
# get values from database
|
||||||
num_total = count_documents(
|
num_total = count_documents(
|
||||||
collection=collection,
|
collection=mongo_collection,
|
||||||
only_with_annotation=False,
|
only_with_annotation=False,
|
||||||
)
|
)
|
||||||
num_handled = count_documents(
|
num_handled = count_documents(
|
||||||
collection=collection,
|
collection=mongo_collection,
|
||||||
only_with_annotation=True,
|
only_with_annotation=True,
|
||||||
)
|
)
|
||||||
limit = int(num_total/20)
|
limit = int(num_total/20)
|
||||||
@@ -118,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
|
||||||
for content, filename in zip(content_list, filename_list):
|
), 'nothing to upload'
|
||||||
try:
|
# handle input
|
||||||
image = VisualCommunication.decode_image(content=content)
|
failed_filename_list = []
|
||||||
vis_com = VisualCommunication(
|
for content, filename in zip(content_list, filename_list):
|
||||||
name=filename,
|
try:
|
||||||
image=image,
|
# decode image content
|
||||||
)
|
image = VisualCommunication.decode_image(content=content)
|
||||||
except Exception as exc:
|
except Exception:
|
||||||
logging.error('failed handling data from file: %s', filename)
|
logging.debug('failed decoding %s', filename)
|
||||||
logging.debug(exc)
|
failed_filename_list.append(filename)
|
||||||
continue
|
continue
|
||||||
vis_com_list.append(vis_com)
|
try:
|
||||||
# upsert documents
|
# instantiate to upload image to minio
|
||||||
success = upsert_visual_communication(
|
vis_com = VisualCommunication.from_name_and_image(
|
||||||
collection=collection,
|
name=filename,
|
||||||
visual_communication_list=vis_com_list,
|
image=image,
|
||||||
)
|
minio_client=minio_client,
|
||||||
if success:
|
)
|
||||||
logging.info('uploaded %s files to database', len(vis_com_list))
|
except Exception as exc:
|
||||||
return 'successfully uploaded images'.title(), None
|
logging.debug(exc)
|
||||||
logging.error('failed uploading images to database')
|
failed_filename_list.append(filename)
|
||||||
return None, 'failed uploading images'.title()
|
continue
|
||||||
|
try:
|
||||||
|
# save to mongodb
|
||||||
|
vis_com.save_to_mongo(collection=mongo_collection)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(exc)
|
||||||
|
failed_filename_list.append(filename)
|
||||||
|
# remove document from minio
|
||||||
|
delete_from_minio(
|
||||||
|
client=minio_client,
|
||||||
|
object_name=vis_com.object_name,
|
||||||
|
)
|
||||||
|
assert len(failed_filename_list) == 0, f"failed uploading:{
|
||||||
|
'\n'.join(failed_filename_list)
|
||||||
|
}"
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(exc)
|
||||||
|
return None, str(exc).title()
|
||||||
|
return 'successfully uploaded images'.title(), None
|
||||||
|
|
||||||
# define callback: cycle visual communication data
|
# define callback: cycle visual communication data
|
||||||
@app.callback(
|
@app.callback(
|
||||||
@@ -217,7 +237,7 @@ def init_app(
|
|||||||
annotations = ModelData.from_annotations(**annotation_map)
|
annotations = ModelData.from_annotations(**annotation_map)
|
||||||
# save data to database
|
# save data to database
|
||||||
upsert_annotation(
|
upsert_annotation(
|
||||||
collection=collection,
|
collection=mongo_collection,
|
||||||
vis_com_name=vis_com_name,
|
vis_com_name=vis_com_name,
|
||||||
annotations=annotations,
|
annotations=annotations,
|
||||||
)
|
)
|
||||||
@@ -231,12 +251,12 @@ def init_app(
|
|||||||
try:
|
try:
|
||||||
# get data
|
# get data
|
||||||
vis_com = get_visual_communication(
|
vis_com = get_visual_communication(
|
||||||
collection=collection,
|
collection=mongo_collection,
|
||||||
with_annotation=False,
|
with_annotation=False,
|
||||||
)
|
)
|
||||||
# set variables
|
# set variables
|
||||||
vis_com_name = vis_com.name
|
vis_com_name = vis_com.name
|
||||||
image_src = vis_com.webencoded_image()
|
image_src = vis_com.webencoded_image(minio_client=minio_client)
|
||||||
if vis_com.prediction is not None:
|
if vis_com.prediction is not None:
|
||||||
# TODO: update to use optional predictions
|
# TODO: update to use optional predictions
|
||||||
pass
|
pass
|
||||||
|
|||||||
+8
-1
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from .app import init_app
|
from .app import init_app
|
||||||
|
from shared.data_store import connect as connect_minio
|
||||||
from shared.database import connect
|
from shared.database import connect
|
||||||
from shared.utils import check_env
|
from shared.utils import check_env
|
||||||
from shared.utils import setup_logging
|
from shared.utils import setup_logging
|
||||||
@@ -17,7 +18,13 @@ setup_logging()
|
|||||||
# connect to database
|
# connect to database
|
||||||
collection, db, client = connect()
|
collection, db, client = connect()
|
||||||
|
|
||||||
|
# connect to minio
|
||||||
|
minio_client = connect_minio()
|
||||||
|
|
||||||
# initialise application
|
# initialise application
|
||||||
app = init_app(collection=collection)
|
app = init_app(
|
||||||
|
mongo_collection=collection,
|
||||||
|
minio_client=minio_client,
|
||||||
|
)
|
||||||
server = app.server
|
server = app.server
|
||||||
server.config.update(SECRET_KEY=os.urandom(24))
|
server.config.update(SECRET_KEY=os.urandom(24))
|
||||||
|
|||||||
Reference in New Issue
Block a user