Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds source file path to IMATLogFile, shows it in metadata #733

Merged
merged 8 commits into from
Dec 2, 2020
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
4 changes: 4 additions & 0 deletions mantidimaging/core/data/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ def log_file(self):

@log_file.setter
def log_file(self, value: IMATLogFile):
if value is not None:
self.metadata[const.LOG_FILE] = value.source_file
elif value is None:
del self.metadata[const.LOG_FILE]
self._log_file = value

def projection_angles(self, max_angle: float = 360.0):
Expand Down
2 changes: 1 addition & 1 deletion mantidimaging/core/data/test/fake_logfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ def generate_logfile() -> IMATLogFile:
"Monitor 3 after: 6078866"
]
]
return IMATLogFile(data)
return IMATLogFile(data, "/tmp/fake")
5 changes: 5 additions & 0 deletions mantidimaging/core/data/test/images_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,8 @@ def test_get_projection_angles_no_logfile(self):
actual = images.projection_angles(275.69)
self.assertEqual(10, len(actual.value))
self.assertAlmostEqual(np.deg2rad(275.69), actual.value[-1], places=4)

def test_metadata_gets_updated_with_logfile(self):
images = generate_images()
images.log_file = generate_logfile()
self.assertEqual(images.log_file.source_file, images.metadata[const.LOG_FILE])
4 changes: 2 additions & 2 deletions mantidimaging/core/io/loader/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ def read_in_file_information(input_path,
return fi


def load_log(log_file) -> IMATLogFile:
def load_log(log_file: str) -> IMATLogFile:
data = []
with open(log_file, 'r') as f:
for line in f:
data.append(line.strip().split(" "))

return IMATLogFile(data)
return IMATLogFile(data, log_file)


def load_p(parameters: ImageParameters, dtype, progress) -> Images:
Expand Down
1 change: 1 addition & 0 deletions mantidimaging/core/operation_history/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
OPERATION_KEYWORD_ARGS = 'kwargs'
OPERATION_DISPLAY_NAME = 'display_name'
PIXEL_SIZE = 'pixel_size'
LOG_FILE = 'log_file'

OPERATION_NAME_COR_TILT_FINDING = 'cor_tilt_finding'
COR_TILT_ROTATION_CENTRE = 'rotation_centre'
Expand Down
7 changes: 6 additions & 1 deletion mantidimaging/core/utility/imat_log_file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class IMATLogColumn(Enum):


class IMATLogFile:
def __init__(self, data: List[List[str]]):
def __init__(self, data: List[List[str]], source_file: str):
self._source_file = source_file
self._data: Dict[IMATLogColumn, List] = {
IMATLogColumn.TIMESTAMP: [],
IMATLogColumn.IMAGE_TYPE_IMAGE_COUNTER: [],
Expand All @@ -46,6 +47,10 @@ def __init__(self, data: List[List[str]]):
self._data[IMATLogColumn.COUNTS_BEFORE].append(line[2])
self._data[IMATLogColumn.COUNTS_AFTER].append(line[3])

@property
def source_file(self) -> str:
return self._source_file

def projection_numbers(self):
proj_nums = numpy.zeros(len(self._data[IMATLogColumn.IMAGE_TYPE_IMAGE_COUNTER]), dtype=numpy.uint32)
for i, angle_str in enumerate(self._data[IMATLogColumn.IMAGE_TYPE_IMAGE_COUNTER]):
Expand Down
18 changes: 15 additions & 3 deletions mantidimaging/core/utility/test/imat_log_file_parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def test_parsing_log_file():
EXPECTED_HEADER_FOR_IMAT_LOG_FILE, ["ignored line"],
["timestamp", "Projection: 0 angle: 0.1", "counts before: 12345", "counts_after: 45678"]
]
logfile = IMATLogFile(test_input)
logfile = IMATLogFile(test_input, "/tmp/fake")
assert len(logfile.projection_angles().value) == 1
assert logfile.projection_angles().value[0] == np.deg2rad(0.1), f"Got: {logfile.projection_angles().value[0]}"
assert logfile.counts().value[0] == (45678 - 12345)
Expand All @@ -25,7 +25,7 @@ def test_counts():
["timestamp", "Projection: 1 angle: 0.1", "counts before: 45678", "counts_after: 84678"],
["timestamp", "Projection: 2 angle: 0.2", "counts before: 84678", "counts_after: 124333"],
]
logfile = IMATLogFile(test_input)
logfile = IMATLogFile(test_input, "/tmp/fake")
assert len(logfile.counts().value) == 3
assert logfile.counts().value[0] == 45678 - 12345
assert logfile.counts().value[1] == 84678 - 45678
Expand All @@ -50,7 +50,7 @@ def test_find_missing_projection_number():
["timestamp", "Projection: 1 angle: 0.1", "counts before: 12345", "counts_after: 45678"],
["timestamp", "Projection: 2 angle: 0.2", "counts before: 12345", "counts_after: 45678"],
]
logfile = IMATLogFile(test_input)
logfile = IMATLogFile(test_input, "/tmp/fake")
assert len(logfile.projection_numbers()) == 3
# nothing missing
logfile.raise_if_angle_missing(["file_000.tif", "file_001.tif", "file_002.tif"])
Expand All @@ -60,3 +60,15 @@ def test_find_missing_projection_number():
assert_raises(RuntimeError, logfile.raise_if_angle_missing, ["file_000.tif", "file_001.tif"])
assert_raises(RuntimeError, logfile.raise_if_angle_missing,
["file_000.tif", "file_001.tif", "file_002.tif", "file_003.tif"])


def test_source_file():
test_input = [
EXPECTED_HEADER_FOR_IMAT_LOG_FILE,
["ignored line"],
["timestamp", "Projection: 0 angle: 0.0", "counts before: 12345", "counts_after: 45678"],
["timestamp", "Projection: 1 angle: 0.1", "counts before: 12345", "counts_after: 45678"],
["timestamp", "Projection: 2 angle: 0.2", "counts before: 12345", "counts_after: 45678"],
]
logfile = IMATLogFile(test_input, "/tmp/fake")
assert logfile.source_file == "/tmp/fake"
17 changes: 13 additions & 4 deletions mantidimaging/gui/windows/main/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,21 @@ def have_active_stacks(self) -> bool:
return len(self.active_stacks) > 0

def add_log_to_sample(self, stack_name: str, log_file: str):
stack = self.get_stack_by_name(stack_name).widget() # type: ignore
stack.presenter.images.log_file = loader.load_log(log_file)
stack.presenter.images.log_file.raise_if_angle_missing(stack.presenter.images.filenames)
stack_dock = self.get_stack_by_name(stack_name)
if stack_dock is None:
raise RuntimeError(f"Failed to get stack with name {stack_name}")

stack: StackVisualiserView = stack_dock.widget() # type: ignore
log = loader.load_log(log_file)
log.raise_if_angle_missing(stack.presenter.images.filenames)
stack.presenter.images.log_file = log

def add_180_deg_to_stack(self, stack_name, _180_deg_file):
stack = self.get_stack_by_name(stack_name).widget() # type: ignore
stack_dock = self.get_stack_by_name(stack_name)
if stack_dock is None:
raise RuntimeError(f"Failed to get stack with name {stack_name}")

stack: StackVisualiserView = stack_dock.widget() # type: ignore
_180_deg = loader.load(file_names=[_180_deg_file])
stack.presenter.images.proj180deg = _180_deg.sample
return _180_deg
29 changes: 29 additions & 0 deletions mantidimaging/gui/windows/main/test/model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ def test_add_log_to_sample(self, load_log: mock.Mock):
stack_mock.return_value.widget.return_value.presenter.images.log_file.raise_if_angle_missing \
.assert_called_once_with(stack_mock.return_value.widget.return_value.presenter.images.filenames)

@mock.patch('mantidimaging.core.io.loader.load_log')
def test_add_log_to_sample_no_stack(self, load_log: mock.Mock):
"""
Test in add_log_to_sample when get_stack_by_name returns None
"""
log_file = "Log file"
stack_name = "stack name"
stack_mock = mock.MagicMock()
self.model.get_stack_by_name = stack_mock
stack_mock.return_value = None

self.assertRaises(RuntimeError, self.model.add_log_to_sample, stack_name=stack_name, log_file=log_file)

stack_mock.assert_called_with(stack_name)

@mock.patch('mantidimaging.core.io.loader.load')
def test_add_180_deg_to_stack(self, load: mock.Mock):
_180_file = "180 file"
Expand All @@ -251,6 +266,20 @@ def test_add_180_deg_to_stack(self, load: mock.Mock):
stack_mock.assert_called_with(stack_name)
self.assertEqual(_180_stack, stack_mock.return_value.widget.return_value.presenter.images.proj180deg)

@mock.patch('mantidimaging.core.io.loader.load')
def test_add_180_deg_to_stack_no_stack(self, load: mock.Mock):
"""
Test in add_180_deg_to_stack when get_stack_by_name returns None
"""
_180_file = "180 file"
stack_name = "stack name"
stack_mock = mock.MagicMock()
self.model.get_stack_by_name = stack_mock
stack_mock.return_value = None

self.assertRaises(RuntimeError, self.model.add_180_deg_to_stack, stack_name=stack_name, _180_deg_file=_180_file)
stack_mock.assert_called_with(stack_name)


if __name__ == '__main__':
unittest.main()
1 change: 1 addition & 0 deletions mantidimaging/gui/windows/operations/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def toggle_average_images(images_):
toggle_show_averaged_image = QAction("Toggle show averaged image", menu)
toggle_show_averaged_image.triggered.connect(lambda: toggle_average_images(images))
menu.addAction(toggle_show_averaged_image)
menu.addSeparator()
self.roi_view.imageItem.menu = menu

self.roi_view.setImage(images.data)
Expand Down
18 changes: 11 additions & 7 deletions mantidimaging/gui/windows/stack_visualiser/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,25 @@ def roi_changed_callback(self, roi: SensibleROI):
self.roi_updated.emit(roi)

def build_context_menu(self) -> QMenu:
actions = [("Set ROI", self.set_roi), ("Copy ROI to clipboard", self.copy_roi_to_clipboard),
("Toggle show averaged image", lambda: self.presenter.notify(SVNotification.TOGGLE_IMAGE_MODE)),
("Create sinograms from stack", lambda: self.presenter.notify(SVNotification.SWAP_AXES)),
actions = [("Show history and metadata", self.show_image_metadata),
("Duplicate whole data", lambda: self.presenter.notify(SVNotification.DUPE_STACK)),
("Duplicate current ROI of data", lambda: self.presenter.notify(SVNotification.DUPE_STACK_ROI)),
("Show history", self.show_image_metadata), ("Mark as projections/sinograms", self.mark_as_),
("Mark as projections/sinograms", self.mark_as_), ("", None),
("Toggle show averaged image", lambda: self.presenter.notify(SVNotification.TOGGLE_IMAGE_MODE)),
("Create sinograms from stack", lambda: self.presenter.notify(SVNotification.SWAP_AXES)),
("Set ROI", self.set_roi), ("Copy ROI to clipboard", self.copy_roi_to_clipboard), ("", None),
("Change window name", self.change_window_name_clicked), ("Goto projection", self.goto_projection),
("Goto angle", self.goto_angle)]

menu = QMenu(self)

for (menu_text, func) in actions:
action = QAction(menu_text, menu)
action.triggered.connect(func)
menu.addAction(action)
if func is None:
menu.addSeparator()
else:
action = QAction(menu_text, menu)
action.triggered.connect(func)
menu.addAction(action)

return menu

Expand Down