Skip to content

Commit

Permalink
fix(EyeVolumeMeta): fixes saving issue by storing all dates as string…
Browse files Browse the repository at this point in the history
… in isoformat to avoid problems with dumping to json and loading
  • Loading branch information
Oli4 committed Mar 31, 2023
1 parent cc43e19 commit 3fc7424
Show file tree
Hide file tree
Showing 10 changed files with 438 additions and 436 deletions.
790 changes: 383 additions & 407 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ packages = [
{include = "eyepy", from = "src"},
]
version = "0.11.0"
description = "The Python package for working with ophthalmological data."
description = "A python package to read, analyse and visualize OCT and fundus data form various sources."
authors = ["Olivier Morelle <oli4morelle@gmail.com>"]
license = "MIT"
readme = "README.md"
Expand Down
28 changes: 14 additions & 14 deletions src/eyepy/core/eyemeta.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ def as_dict(self) -> dict:
"""
data = self._store.copy()
for key in ['visit_date', 'exam_time']:
if key in data.keys() and data[key] is not None:
data[key] = data[key].isoformat()
return data

def __getitem__(self, key: str) -> Any:
Expand All @@ -47,7 +44,8 @@ def __len__(self) -> int:
return len(self._store)

def __str__(self) -> str:
return f'{os.linesep}'.join([f'{f}: {self[f]}' for f in self if f != '__empty'])
return f'{os.linesep}'.join(
[f'{f}: {self[f]}' for f in self if f != '__empty'])

def __repr__(self) -> str:
return self.__str__()
Expand All @@ -56,7 +54,8 @@ def __repr__(self) -> str:
class EyeEnfaceMeta(EyeMeta):
""""""

def __init__(self, scale_x: float, scale_y: float, scale_unit: str, **kwargs) -> None:
def __init__(self, scale_x: float, scale_y: float, scale_unit: str,
**kwargs) -> None:
"""A dict with required keys to hold meta data for enface images of the
eye.
Expand All @@ -66,9 +65,10 @@ def __init__(self, scale_x: float, scale_y: float, scale_unit: str, **kwargs) ->
scale_unit: Unit of the scale. e.g. µm if scale is given in µm/pixel
**kwargs:
"""
super().__init__(
scale_x=scale_x, scale_y=scale_y, scale_unit=scale_unit, **kwargs
)
super().__init__(scale_x=scale_x,
scale_y=scale_y,
scale_unit=scale_unit,
**kwargs)

@classmethod
def from_dict(cls, data: dict) -> 'EyeEnfaceMeta':
Expand Down Expand Up @@ -104,9 +104,12 @@ def __init__(
pos_unit: Unit of the positions
**kwargs:
"""
super().__init__(
start_pos=start_pos, end_pos=end_pos, pos_unit=pos_unit, **kwargs
)
start_pos = tuple(start_pos)
end_pos = tuple(end_pos)
super().__init__(start_pos=start_pos,
end_pos=end_pos,
pos_unit=pos_unit,
**kwargs)


class EyeVolumeMeta(EyeMeta):
Expand Down Expand Up @@ -161,7 +164,4 @@ def from_dict(cls, data: dict) -> 'EyeVolumeMeta':
"""
data['bscan_meta'] = [EyeBscanMeta(**d) for d in data['bscan_meta']]
for key in ['visit_date', 'exam_time']:
if key in data.keys() and data[key] is not None:
data[key] = datetime.datetime.fromisoformat(data[key])
return cls(**data)
2 changes: 0 additions & 2 deletions src/eyepy/core/eyevolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
from eyepy.core.eyemeta import EyeBscanMeta
from eyepy.core.eyemeta import EyeEnfaceMeta
from eyepy.core.eyemeta import EyeVolumeMeta
from eyepy.core.plotting import plot_scalebar
from eyepy.core.plotting import plot_watermark
from eyepy.core.utils import intensity_transforms

logger = logging.getLogger('eyepy.core.eyevolume')
Expand Down
9 changes: 5 additions & 4 deletions src/eyepy/io/he/e2e_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,13 @@ class DateTimeAdapter(cs.Adapter):
minute=0)

def _decode(self, obj: bytes, context, path):
return self.start_epoch + datetime.timedelta(
seconds=cs.Int64ul.parse(obj) * 1e-7)
return (self.start_epoch + datetime.timedelta(
seconds=cs.Int64ul.parse(obj) * 1e-7)).isoformat()

def _encode(self, obj: datetime.datetime, context, path):
def _encode(self, obj: str, context, path):
return cs.Int64ul.build(
int((obj - self.start_epoch).total_seconds() * 1e7))
int((datetime.datetime.fromisoformat(obj) -
self.start_epoch).total_seconds() * 1e7))


LocalizerNIR = LocalizerNIRAdapter(cs.Bytes(cs.this.n_values))
Expand Down
2 changes: 1 addition & 1 deletion src/eyepy/io/he/e2e_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ def get_meta(self) -> EyeVolumeMeta:
scale_z=1, #get_bscan_spacing(bscan_meta) if
#(bscan_meta[0]["scan_pattern"] not in [1, 2]) else 0.03,
scale_unit='px',
laterality=self.laterality,
laterality=self.laterality(),
visit_date=None,
exam_time=None,
bscan_meta=bscan_meta,
Expand Down
1 change: 0 additions & 1 deletion src/eyepy/io/he/xml_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from eyepy.core.eyevolume import EyeVolume
from eyepy.io.utils import _compute_localizer_oct_transform
from eyepy.io.utils import _get_date_from_xml
from eyepy.io.utils import _get_datetime_from_xml
from eyepy.io.utils import _get_first_as_float
from eyepy.io.utils import _get_first_as_int
from eyepy.io.utils import _get_first_as_str
Expand Down
7 changes: 4 additions & 3 deletions src/eyepy/io/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def _get_date_from_xml(elements):
except IndexError:
year = month = day = 1

return datetime(year, month, day).date()
return datetime(year, month, day).isoformat()


def _compute_localizer_oct_transform(
Expand Down Expand Up @@ -137,10 +137,11 @@ def get_date_adapter(construct, epoch, second_frac):
class DateAdapter(cs.Adapter):

def _decode(self, obj, context, path):
return epoch + timedelta(seconds=obj * second_frac)
return (epoch + timedelta(seconds=obj * second_frac)).isoformat()

def _encode(self, obj, context, path):
return int((obj - epoch).total_seconds() / second_frac)
return (datetime.fromisoformat(obj) -
epoch).total_seconds() / second_frac

return DateAdapter(construct)

Expand Down
7 changes: 4 additions & 3 deletions tests/test_eyevolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,13 @@ def test_delete_voxel_annotation(eyevolume):
assert 'delete_volume' not in eyevolume[2].area_maps


# Saving is not possible when executed by GitHub Actions
#def test_save_load(eyevolume, tmp_path):
# # Save
# eyevolume.save(tmp_path / "test.eye")
# eyevolume.save(tmp_path / 'test.eye')
# # Load
# eyevolume2 = ep.EyeVolume.load(tmp_path / "test.eye")
# eyevolume2 = ep.EyeVolume.load(tmp_path / 'test.eye')
# # Test whether loaded eyevolume is the same as the original
# assert len(eyevolume.meta) == len(eyevolume2.meta)
# assert eyevolume.meta == eyevolume2.meta
# assert len(eyevolume.layers) == len(eyevolume2.layers)
# assert len(eyevolume.volume_maps) == len(eyevolume2.volume_maps)
26 changes: 26 additions & 0 deletions tests/test_eyevolumemeta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import eyepy as ep


def test_eyevolumemeta_json_conversion():
meta_dict = {
'scale_z':
1,
'scale_x':
1,
'scale_y':
1,
'scale_unit':
'px',
'bscan_meta': [{
'start_pos': (0, 0),
'end_pos': [10, 0],
'pos_unit': 'px'
}, {
'start_pos': (0, 1),
'end_pos': [10, 1],
'pos_unit': 'px'
}]
}

evm = ep.EyeVolumeMeta.from_dict(meta_dict)
assert evm.as_dict() == meta_dict

0 comments on commit 3fc7424

Please sign in to comment.