Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
there is no file at the path. Scripts that relied on these calls
silently continuing must catch the exceptions.

## Data containers

- Removed the unused `DataStore` and `ProjectData` classes from
`easyreflectometry.data`. Their `as_dict`/`from_dict` methods had
never worked (the former raised a `TypeError`, the latter recursed
into a `KeyError`) and nothing in the library, the GUI or the docs
used them; `Project` keeps experiments in its own dictionary. Code
that imported `ProjectData` from `easyreflectometry.data` must switch
to `Project`.
- `Project.as_dict` now always records an experiment's name and model,
not only when the experiment carries x-uncertainties. Previously an
experiment with `xe` set to `None` saved without them and the project
then failed to load.

## Models

- `ModelCollection(interface=...)` with no models now builds its default
Expand Down
2 changes: 0 additions & 2 deletions src/easyreflectometry/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# SPDX-License-Identifier: BSD-3-Clause

from .data_store import DataSet1D
from .data_store import ProjectData
from .measurement import dataset_from_datagroup
from .measurement import load
from .measurement import load_as_dataset
Expand All @@ -17,7 +16,6 @@
'load_as_dataset',
'dataset_from_datagroup',
'merge_datagroups',
'ProjectData',
'DataSet1D',
'PolarizedDataSet',
'channel_from_orso_polarization',
Expand Down
70 changes: 0 additions & 70 deletions src/easyreflectometry/data/data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,84 +3,14 @@

__author__ = 'github.com/wardsimon'

from collections.abc import Sequence
from typing import Optional
from typing import TypeVar
from typing import Union

import numpy as np
from easyscience.io import SerializerComponent
from easyscience.io import SerializerDict

from easyreflectometry.model import Model

T = TypeVar('T')


class ProjectData(SerializerComponent):
def __init__(self, name='DataStore', exp_data=None, sim_data=None):
"""Init function."""
self.name = name
if exp_data is None:
exp_data = DataStore(name='Exp Datastore')
if sim_data is None:
sim_data = DataStore(name='Sim Datastore')
self.exp_data = exp_data
self.sim_data = sim_data


class DataStore(Sequence, SerializerComponent):
def __init__(self, *args, name='DataStore'):
"""Init function."""
self.name = name
self.items = list(args)
self.show_legend = False

def __getitem__(self, i: int) -> T:
"""Getitem function."""
return self.items.__getitem__(i)

def __len__(self) -> int:
"""Len function."""
return len(self.items)

def __setitem__(self, key, value):
"""Setitem function."""
self.items[key] = value

def __delitem__(self, key):
"""Delitem function."""
del self.items[key]

def append(self, *args):
"""Append function."""
self.items.append(*args)

def as_dict(self, skip: list = []) -> dict:
"""As dict."""
this_dict = super(DataStore, self).as_dict(self, skip=skip)
this_dict['items'] = [item.as_dict() for item in self.items if hasattr(item, 'as_dict')]

@classmethod
def from_dict(cls, d):
"""From dict."""
items = d['items']
del d['items']
obj = cls.from_dict(d)
decoder = SerializerDict()
obj.items = [decoder.decode(item) for item in items]
return obj

@property
def experiments(self):
"""Experiments function."""
return [self[idx] for idx in range(len(self)) if self[idx].is_experiment]

@property
def simulations(self):
"""Simulations function."""
return [self[idx] for idx in range(len(self)) if self[idx].is_simulation]


class DataSet1D(SerializerComponent):
def __init__(
Expand Down
6 changes: 3 additions & 3 deletions src/easyreflectometry/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1944,8 +1944,8 @@
]
if experiment.xe is not None:
project_dict['experiments'][key].append(list(experiment.xe))
project_dict['experiments_models'][key] = experiment.model.name
project_dict['experiments_names'][key] = experiment.name
project_dict['experiments_models'][key] = experiment.model.name
project_dict['experiments_names'][key] = experiment.name

Check warning on line 1948 in src/easyreflectometry/project.py

View check run for this annotation

Codecov / codecov/patch

src/easyreflectometry/project.py#L1947-L1948

Added lines #L1947 - L1948 were not covered by tests

@staticmethod
def _as_dict_add_polarized_experiment(project_dict: dict, key: int, experiment: PolarizedDataSet) -> None:
Expand Down Expand Up @@ -2068,7 +2068,7 @@
x=raw[0],
y=raw[1],
ye=raw[2],
xe=raw[3],
xe=raw[3] if len(raw) > 3 else None,
model=self._models[project_dict['experiments_models'][key]],
auto_background=False,
)
Expand Down
167 changes: 0 additions & 167 deletions tests/data/test_data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
from numpy.testing import assert_array_equal

from easyreflectometry.data.data_store import DataSet1D
from easyreflectometry.data.data_store import DataStore
from easyreflectometry.data.data_store import ProjectData


class TestDataSet1D:
Expand Down Expand Up @@ -149,168 +147,3 @@ def test_default_error_arrays_when_none(self):
# Then
assert_array_equal(data.ye, np.zeros(3))
assert_array_equal(data.xe, np.zeros(3))


class TestDataStore:
def test_constructor_default(self):
# When
store = DataStore()

# Then
assert store.name == 'DataStore'
assert len(store) == 0
assert store.show_legend is False

def test_constructor_with_name(self):
# When
store = DataStore(name='TestStore')

# Then
assert store.name == 'TestStore'

def test_constructor_with_items(self):
# Given
item1 = DataSet1D(name='item1')
item2 = DataSet1D(name='item2')

# When
store = DataStore(item1, item2, name='TestStore')

# Then
assert len(store) == 2
assert store[0] == item1
assert store[1] == item2

def test_getitem(self):
# Given
item = DataSet1D(name='test')
store = DataStore(item)

# When/Then
assert store[0] == item

def test_setitem(self):
# Given
item1 = DataSet1D(name='item1')
item2 = DataSet1D(name='item2')
store = DataStore(item1)

# When
store[0] = item2

# Then
assert store[0] == item2

def test_delitem(self):
# Given
item1 = DataSet1D(name='item1')
item2 = DataSet1D(name='item2')
store = DataStore(item1, item2)

# When
del store[0]

# Then
assert len(store) == 1
assert store[0] == item2

def test_append(self):
# Given
store = DataStore()
item = DataSet1D(name='test')

# When
store.append(item)

# Then
assert len(store) == 1
assert store[0] == item

def test_len(self):
# Given
store = DataStore()

# When/Then
assert len(store) == 0

store.append(DataSet1D())
assert len(store) == 1

def test_experiments_property(self):
# Given
exp_data = DataSet1D(name='exp', model=Mock())
sim_data = DataSet1D(name='sim')
store = DataStore(exp_data, sim_data)

# When
experiments = store.experiments

# Then
assert len(experiments) == 1
assert experiments[0] == exp_data

def test_simulations_property(self):
# Given
exp_data = DataSet1D(name='exp', model=Mock())
sim_data = DataSet1D(name='sim')
store = DataStore(exp_data, sim_data)

# When
simulations = store.simulations

# Then
assert len(simulations) == 1
assert simulations[0] == sim_data

def test_as_dict_with_serializable_items(self):
# Given
mock_item = Mock()
mock_item.as_dict.return_value = {'test': 'data'}
store = DataStore(mock_item, name='TestStore')

# When - The as_dict method has implementation issues, so just test it exists
# and can be called without crashing
assert hasattr(store, 'as_dict')
assert callable(getattr(store, 'as_dict'))

def test_from_dict_class_method(self):
# Given - Test that the method exists
# The actual implementation has dependencies that make it hard to test in isolation

# When/Then - Just verify the method exists
assert hasattr(DataStore, 'from_dict')
assert callable(getattr(DataStore, 'from_dict'))


class TestProjectData:
def test_constructor_default(self):
# When
project = ProjectData()

# Then
assert project.name == 'DataStore'
assert isinstance(project.exp_data, DataStore)
assert isinstance(project.sim_data, DataStore)
assert project.exp_data.name == 'Exp Datastore'
assert project.sim_data.name == 'Sim Datastore'

def test_constructor_with_name(self):
# When
project = ProjectData(name='TestProject')

# Then
assert project.name == 'TestProject'

def test_constructor_with_custom_datastores(self):
# Given
exp_store = DataStore(name='CustomExp')
sim_store = DataStore(name='CustomSim')

# When
project = ProjectData(name='TestProject', exp_data=exp_store, sim_data=sim_store)

# Then
assert project.exp_data == exp_store
assert project.sim_data == sim_store
assert project.exp_data.name == 'CustomExp'
assert project.sim_data.name == 'CustomSim'
Loading
Loading