Skip to content

Commit

Permalink
Handle multiple reduction runs (#500)
Browse files Browse the repository at this point in the history
* get matching cal/norm versions

* update workflow warnings

* update code coverage
  • Loading branch information
rboston628 committed Nov 22, 2024
1 parent f0f50da commit 9f6d60b
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 16 deletions.
38 changes: 22 additions & 16 deletions src/snapred/ui/workflow/ReductionWorkflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,35 +189,41 @@ def _triggerReduction(self, workflowPresenter):
self._keeps.update(loadedCalibrations)
self._keeps.update(loadedNormalizations)

if len(loadedNormalizations) > 1 and None in normVersions:
distinctNormVersions = set(normVersions.values())
if len(distinctNormVersions) > 1 and None in distinctNormVersions:
raise RuntimeError(
"Some of your workspaces require Artificial Normalization. "
"SNAPRed can currently only handle the situation where all, or none "
"of the runs require Artificial Normalization. Please clear the list "
"and try again."
)

for runNumber in self.runNumbers:
self._artificialNormalizationView.updateRunNumber(runNumber)
request_ = self._createReductionRequest(runNumber)

# Validate reduction; if artificial normalization is needed, handle it
response = self.request(path="reduction/validate", payload=request_)
if ContinueWarning.Type.MISSING_NORMALIZATION in self.continueAnywayFlags:
# Validate reduction; if artificial normalization is needed, handle it
# NOTE: this logic ONLY works because we are forbidding mixed cases of artnorm or loaded norm
response = self.request(path="reduction/validate", payload=request_)
if ContinueWarning.Type.MISSING_NORMALIZATION in self.continueAnywayFlags:
if len(self.runNumbers) > 1:
raise RuntimeError(
"Currently, Artificial Normalization can only be performed on a "
"single run at a time. Please clear your run list and try again."
)
for runNumber in self.runNumbers:
self._artificialNormalizationView.updateRunNumber(runNumber)
self._artificialNormalizationView.showAdjustView()
request_ = self._createReductionRequest(runNumber)
response = self.request(path="reduction/grabWorkspaceforArtificialNorm", payload=request_)
self._artificialNormalization(workflowPresenter, response.data, runNumber)
else:
# Proceed with reduction if artificial normalization is not needed
else:
for runNumber in self.runNumbers:
self._artificialNormalizationView.showSkippedView()
request_ = self._createReductionRequest(runNumber)
response = self.request(path="reduction/", payload=request_)
if response.code == ResponseCode.OK:
record, unfocusedData = response.data.record, response.data.unfocusedData
self._finalizeReduction(record, unfocusedData)
# after each run, clean workspaces except groupings, calibrations, normalizations, and outputs
self._keeps.update(self.outputs)
self._clearWorkspaces(exclude=self._keeps, clearCachedWorkspaces=True)
workflowPresenter.advanceWorkflow()
self._finalizeReduction(response.data.record, response.data.unfocusedData)
# after each run, clean workspaces except groupings, calibrations, normalizations, and outputs
self._keeps.update(self.outputs)
self._clearWorkspaces(exclude=self._keeps, clearCachedWorkspaces=True)
workflowPresenter.advanceWorkflow()
# SPECIAL FOR THE REDUCTION WORKFLOW: clear everything _except_ the output workspaces
# _before_ transitioning to the "save" panel.
# TODO: make '_clearWorkspaces' a public method (i.e make this combination a special `cleanup` method).
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/backend/service/test_CalibrationService.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,33 @@ def test_fitPeaks(self, FitMultiplePeaksRecipe):
res = self.instance.fitPeaks(request)
assert res == FitMultiplePeaksRecipe.return_value.executeRecipe.return_value

def test_matchRuns(self):
self.instance.dataFactoryService.getThisOrLatestCalibrationVersion = mock.Mock(
side_effect=[mock.sentinel.version1, mock.sentinel.version2, mock.sentinel.version3],
)
request = mock.Mock(runNumbers=[mock.sentinel.run1, mock.sentinel.run2], useLiteMode=True)
response = self.instance.matchRunsToCalibrationVersions(request)
assert response == {
mock.sentinel.run1: mock.sentinel.version1,
mock.sentinel.run2: mock.sentinel.version2,
}

def test_fetchRuns(self):
mockCalibrations = {
mock.sentinel.run1: mock.sentinel.version1,
mock.sentinel.run2: mock.sentinel.version2,
mock.sentinel.run3: mock.sentinel.version2,
}
mockGroceries = [mock.sentinel.grocery1, mock.sentinel.grocery2, mock.sentinel.grocery2]
self.instance.matchRunsToCalibrationVersions = mock.Mock(return_value=mockCalibrations)
self.instance.groceryService.fetchGroceryList = mock.Mock(return_value=mockGroceries)
self.instance.groceryClerk = mock.Mock()

request = mock.Mock(runNumbers=[mock.sentinel.run1, mock.sentinel.run2], useLiteMode=True)
groceries, cal = self.instance.fetchMatchingCalibrations(request)
assert groceries == {mock.sentinel.grocery1, mock.sentinel.grocery2}
assert cal == mockCalibrations

def test_initializeState(self):
testCalibration = DAOFactory.calibrationParameters()
mockInitializeState = mock.Mock(return_value=testCalibration.instrumentState)
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/backend/service/test_NormalizationService.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,33 @@ def test_smoothDataExcludingPeaks(
SmoothingParameter=mockRequest.smoothingParameter,
)

def test_matchRuns(self):
self.instance.dataFactoryService.getThisOrLatestNormalizationVersion = mock.Mock(
side_effect=[mock.sentinel.version1, mock.sentinel.version2],
)
request = mock.Mock(runNumbers=[mock.sentinel.run1, mock.sentinel.run2], useLiteMode=True)
response = self.instance.matchRunsToNormalizationVersions(request)
assert response == {
mock.sentinel.run1: mock.sentinel.version1,
mock.sentinel.run2: mock.sentinel.version2,
}

def test_fetchRuns(self):
mockCalibrations = {
mock.sentinel.run1: mock.sentinel.version1,
mock.sentinel.run2: mock.sentinel.version2,
mock.sentinel.run3: mock.sentinel.version2,
}
mockGroceries = [mock.sentinel.grocery1, mock.sentinel.grocery2, mock.sentinel.grocery2]
self.instance.matchRunsToNormalizationVersions = mock.Mock(return_value=mockCalibrations)
self.instance.groceryService.fetchGroceryList = mock.Mock(return_value=mockGroceries)
self.instance.groceryClerk = mock.Mock()

request = mock.Mock(runNumbers=[mock.sentinel.run1, mock.sentinel.run2], useLiteMode=True)
groceries, cal = self.instance.fetchMatchingNormalizations(request)
assert groceries == {mock.sentinel.grocery1, mock.sentinel.grocery2}
assert cal == mockCalibrations

def test_normalizationAssessment(self):
self.instance = NormalizationService()
self.instance.sousChef = SculleryBoy()
Expand Down

0 comments on commit 9f6d60b

Please sign in to comment.