From b69c90345f6c88b663bebe5e64f6103c3fd0297c Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Tue, 3 Nov 2020 15:45:00 +0000 Subject: [PATCH] Removed full capture metadata from top-level resource list --- .../api/v2/views/captures.py | 29 ++++++++----- openflexure_microscope/captures/capture.py | 43 ++++++++++++++++--- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/openflexure_microscope/api/v2/views/captures.py b/openflexure_microscope/api/v2/views/captures.py index 9659fcef..58ccf0d0 100644 --- a/openflexure_microscope/api/v2/views/captures.py +++ b/openflexure_microscope/api/v2/views/captures.py @@ -17,9 +17,9 @@ class InstrumentSchema(Schema): state = fields.Dict() -class CaptureMetadataImageSchema(Schema): +class ImageSchema(Schema): id = fields.UUID() - acquisitionDate = fields.String(format="date") + time = fields.String(format="date") format = fields.String() name = fields.String() tags = fields.List(fields.String()) @@ -30,19 +30,20 @@ class CaptureMetadataSchema(Schema): experimenter = fields.Dict() # TODO: Make schema experimenterGroup = fields.Dict() # TODO: Make schema dataset = fields.Dict() # TODO: Make schema - image = fields.Nested(CaptureMetadataImageSchema()) + image = fields.Nested(ImageSchema()) instrument = fields.Nested(InstrumentSchema()) -class CaptureSchema(Schema): - id = fields.String() +class CaptureSchema(ImageSchema): + """ + Schema containing only basic attributes required + for interacting with a capture. Additional attributes + are returned by using FullCaptureSchema + """ + dataset = fields.Dict() # TODO: Make schema file = fields.String( data_key="path", description="Path of file on microscope device" ) - exists = fields.Bool(data_key="available") - name = fields.String() - metadata = fields.Nested(CaptureMetadataSchema()) - links = fields.Dict() @pre_dump @@ -91,6 +92,14 @@ class CaptureSchema(Schema): return data +class FullCaptureSchema(CaptureSchema): + """ + Capture schema including metadata. We exclude this by default + since it can become huge due to complex settings including + lens shading tables and CSM matrices. + """ + metadata = fields.Nested(CaptureMetadataSchema()) + class CaptureList(PropertyView): tags = ["captures"] @@ -108,7 +117,7 @@ class CaptureList(PropertyView): class CaptureView(View): tags = ["captures"] - @marshal_with(CaptureSchema()) + @marshal_with(FullCaptureSchema()) def get(self, id_): """ Description of a single image capture diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index 2b4529a7..794f70aa 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -120,7 +120,8 @@ class CaptureObject(object): # Store a nice ID self.id = uuid.uuid4() #: str: Unique capture ID logging.debug("Created CaptureObject {}".format(self.id)) - self.datetime = datetime.datetime.now() + + self.time = datetime.datetime.now() # Create file name. Default to UUID self.format = None @@ -130,12 +131,15 @@ class CaptureObject(object): if not os.path.exists(self.filefolder): os.makedirs(self.filefolder) - # Dictionary for adding top-level metadata (cannmot be accessed through web API) + # Dictionary for adding top-level metadata + # This can ONLY be modified by the server application + # Top level metadata cannot be modified via the web API self._metadata = {} - # Dictionary for storing custom annotations + # Can be modified via the web API self.annotations = {} # List for storing tags + # Can be modified via the web API self.tags = [] def write(self, s): @@ -173,6 +177,22 @@ class CaptureObject(object): else: return False + + @property + def dataset(self) -> str: + """ + If capture is part of a dataset, return basic dataset info. + Otherwise return None + """ + dataset = self.metadata.get("dataset") + if not dataset: + return None + return { + "id": dataset.get("id"), + "name": dataset.get("name"), + "type": dataset.get("type") + } + # HANDLE TAGS def put_tags(self, tags: list): """ @@ -199,7 +219,7 @@ class CaptureObject(object): self.save_metadata() - # HANDLE METADATA + # HANDLE ANNOTATIONS def put_annotations(self, data: dict) -> None: """ @@ -211,6 +231,13 @@ class CaptureObject(object): self.annotations.update(data) self.save_metadata() + def delete_annotation(self, key: str) -> None: + if key in self.annotations: + del self.annotations[key] + self.save_metadata() + + # HANDLE METADATA + def put_metadata(self, data: dict) -> None: """ Merge root metadata from a passed dictionary into the capture metadata, and saves. @@ -230,6 +257,8 @@ class CaptureObject(object): """ self._metadata = data + # BULK OPERATIONS + def put_and_save( self, tags: list = None, annotations: dict = None, metadata: dict = None ): @@ -273,6 +302,8 @@ class CaptureObject(object): piexif.insert(exif_bytes, self.file) logging.info("Finished saving metadata to %s", self.file) + # PROPERTIES + @property def metadata(self) -> dict: """ @@ -283,7 +314,7 @@ class CaptureObject(object): "image": { "id": self.id, "name": self.name, - "acquisitionDate": self.datetime.isoformat(), + "time": self.time.isoformat(), "format": self.format, "tags": self.tags, "annotations": self.annotations, @@ -354,6 +385,8 @@ class CaptureObject(object): piexif.insert(exif_bytes, self.file) return io.BytesIO(thumbnail) + # FILE MANAGEMENT + def save(self) -> None: """Write stream to file, and save/update metadata file""" # If a stream OR file exists, save the metadata file