-
-
Notifications
You must be signed in to change notification settings - Fork 152
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
Fixed Price/Spec/Interim not set in AR Manage Analyses #593
Conversation
|
||
# Bail out if the AR is in a frozen state | ||
ar_state = api.get_workflow_status_of(instance, state_var="review_state") | ||
if ar_state not in ALLOWED_STATES: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inactive
or cancelled
states shouldn't be allowed. I would prefer to not give the responsibility to the developer to know about states unless strictly necessary. Thus, instead of having a var ALLOWED_STATES
I suggest to use the functions isActive
and wasTransitionPerformed
from bika.lims.workflow
to replace this condition by the following:
if not isActive(instance) or wasTransitionPerformed(instance, 'verify'):
raise ValueError(
...
NB: I know, camelCase kind of functions, but no worries, bika.lims.workflow
will be revisited soon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if I missed now something, but inactive
and cancelled
would raise here a ValueError
.
Regarding isActive
there is a pendant in the API already:
https://github.com/senaite/senaite.core/blob/master/bika/lims/api.py#L834
Didn't know yet about the wasTransitionPerformed
function...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, regarding inactive
and cancelled
, these are in another Workflow... Got it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re isActive
I know there is a function for this in the api already, but I did notice this after isActive
was there. But no worries, I have in mind to conciliate them soon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason of a wasTransitionPerformed
is that verify
transition (as well as others) is a "no-way-back" transition, so this code will always work also if an additional state is added later (e.g. "printed" after/before "published", etc.)
# Convert the items to a valid list of AnalysisServices | ||
services = filter(None, map(self._to_service, items)) | ||
# Service UIDs | ||
service_uids = map(api.get_uid, services) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it shouldn't be necessary, but in the original file, we were also removing empties:
service_uids = filter(None, service_uids)
It was introduced here ARAnalysesField setter doesn't accept Analysis objects #431. I don't recall about the reason (if any).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 159 removes empties, so 161 should never contain empty UIDs, unless the DB is broke;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hehe, maybe this was the reason of adding that check... It was bad, indeed.
continue | ||
|
||
# Skip Analyses in frozen states | ||
if self._is_frozen(analysis): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_is_frozen
function is only used once, here. As per a previous comment, cancelled
and inactive
analyses should be considered as "frozen" states too. I'd replace this condition by the following:
if not isActive(instance) or wasTransitionPerformed(instance, 'verify'):
Or make this the logic inside _is_frozen
and use it in L153 too
continue | ||
|
||
# If it is assigned to a worksheet, unassign it before deletion. | ||
if self._is_assigned_to_worksheet(analysis): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following should give you the same result:
if getCurrentState(analysis) == 'assigned':
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer using small functions with mnemonic names to make the code more understandable and maintainable, even if it is used just once. I see them as small component blocks which I can reuse elsewhere and finally factor it out from here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine!
:param service: Analysis Service Object | ||
""" | ||
service_interims = service.getInterimFields() | ||
analysis.setInterimFields(service_interims) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interim fields assigned to an AnalysisService are a copy of the interim fields of the calculation the service is associated to: when you create/edit an AnalysisService and assign a calculation, the system gets the interim fields from the Calculation and assigns them to the field with the same name of the Analysis Service. Although done with javascript (much better if it was done in python), this is fine, but what happens if you change the Calculation after it was previously assigned to an Analysis Service? The Analysis Service still has the interim fields from the calculation before it was changed. If I am not wrong, this is what the previous code was trying to overcome. I agree the previous approach was odd, but this scenario must be considered. I suggest to just update the interim fields from associated Analysis Services when a Calculation is updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if you can add a doctest with this use-case, then it would be awesome!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interim fields of the Analysis are only taken from the Analysis Service only, see here:
If the Interim Fields of the Calculation is changed, it reflects directly to each Analysis of the AS – not sure if dragons are sleeping here.
The code before did actually mesh the interims of both together in the Analysis, which is in my opinion wrong (if it would have set it, which it did not)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure the interim fields from AS get updated automatically when they are changed on his Calculation counterpart. If yes, then I totally agree with analysis.setInterimFields(service.getInterimFields())
. Give me a couple of hours to double-check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AS interims are currently not affected by the calculation Interims, see here in the doctest:
Therefore, I think putting them together on the Analysis doesn't make sense, but I've not analyzed the dependent code parts any further (If those merge the interims together or not)
|
||
>>> analysisservice4.setInterimFields([interim2]) | ||
>>> map(lambda x: x["keyword"], analysisservice4.getInterimFields()) | ||
['B'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to omit the tricky workaround in ARAnalysisField
regarding interim (something I agree), then this should be like follows:
>>> map(lambda x: x["keyword"], analysisservice4.getInterimFields())
['A', 'B']
Remember that at AS level, you can override the default value for an Interim defined in the Calculation, so you could have an interim called "Ca" in calculation with a default value of 0.1 and with the attribute "Hidden" checked whilst you could have an interim with the same keyword ("Ca"), but with default value 0.5 and "Hidden=False". So, InterimFields set at AS level have always priority over the interims set at Calculation level.
If you add an interim to a Calculation, InterimFields
field from all Analysis Services that have that Calculation assigned, must be updated accordingly. New interims that were not present in AnalysisService
must be added accordingly (otherwise, the system will not be able to calculate the result). Those interims removed from Calculation
must be removed from the AnalysisService
. Those interims that were added directly in the Analysis Service that do not match with the interims set in Calcultion
must be preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could argue that well, leaving this like this, the system still works. Yes, agree, but because we have to always take this priority thing with interims into consideration (so at some point, it works by accident). See this as an example:
https://github.com/senaite/senaite.core/blob/master/bika/lims/browser/calcs.py#L190-L205
First, we get the interim fields from the Calculation
and just after mapping them, we override with the values from the analysis.... Wouldn't it be better to just get rid of calc.getInterimFields()
and reduce complexity? The code would look much better and clean, with just only one loop.
The Analysis also inherits the Interim Fields of the Analysis Service: | ||
|
||
>>> map(lambda x: x["keyword"], analysis.getInterimFields()) | ||
['B'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I don't want to rely on Calculation
to get the interim fields from the analysis. So, I'd expect the following:
>>> map(lambda x: x["keyword"], analysis.getInterimFields())
['A', 'B']
I always assume that analysis.getInterimFields()
always return the interim fields that must be used. I don't care about what Calculation says on this regard, because interim fields from analysis always have priority over the ones that are defined in Calculation.
A reminder that the following things must be addressed in a further PR:
See these comment threads for more detailed info: |
Description of the issue/feature this PR addresses
Linked issue: #544
Note for Reviewers:
Please step through the Commits for a better understanding
Current behavior before PR
Desired behavior after PR is merged
Field set the all offered inputs on the Analyses
--
I confirm I have tested this PR thoroughly and coded it according to PEP8
and Plone's Python styleguide standards.