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

Refactored and fixed inconsistencies with Analysis Turnaround Time logic #1032

Merged
merged 25 commits into from
Oct 1, 2018

Conversation

xispa
Copy link
Member

@xispa xispa commented Sep 25, 2018

Description of the issue/feature this PR addresses

Superseds the following PRs:

Revisit, fixing and cleanup of functions and logic related with Analysis Turnaround Time.

--
I confirm I have tested this PR thoroughly and coded it according to PEP8
and Plone's Python styleguide standards.

@xispa xispa added P1: Urgent Cleanup 🧹 Code cleanup and refactoring labels Sep 25, 2018
@xispa xispa requested a review from ramonski September 25, 2018 22:33
@@ -591,6 +591,8 @@ def _folder_item_duedate(self, analysis_brain, item):
# returns the date when the ReferenceSample expires. If the analysis is
# a duplicate, `getDueDate` returns the due date of the source analysis
due_date = analysis_brain.getDueDate
if not due_date:
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better return None to be more explicit

:return: the time in minutes that exceeds the maximum turnaround time
:rtype: int
"""
return -self.getEarliness()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why - here? This does not match with the docstring

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getEarliness is the remaining time in minutes for the TAT of the analysis to be reached. If reached (the analysis is late), getEarliness returns the time in negative..... so getLateness becomes the negative of getEarliness

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it, thanks

if (resultdate and resultdate > duedate) \
or (not resultdate and DateTime() > duedate):
for analysis in self.getAnalyses(full_objects=True, retracted=False):
if analysis.isLateAnalysis():
return True
return False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do something like this:

any(filter(lambda an: an.isLateAnalysis(), self.getAnalyses(full_objects=True, retracted=False)))

Copy link
Member Author

@xispa xispa Sep 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first did something similar, but more bizarre:

ans = filter(lambda an: an.isLateAnalysis(), self.getAnalyses(full_objects=True, retracted=False))
return len(ans) > 0

but later, I decided to use the for loop because there is no need to filter all analyses: I thought the function would be faster if I return immediately after the first late analysis is found.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, good point

@@ -1265,6 +1266,33 @@ def to_date(value, default=None):
return to_date(default)


def to_minutes(days=0, hours=0, minutes=0, seconds=0, milliseconds=0,
to_int=True):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave in general the type conversion to the calling function and return only a string here.
Also that to_int does actually also a round which is not directly obvious to the caller

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm, don't like to do int(round(api.get_minutes(**kwargs))) everywhere. I suggest to change the param name from to_int to round_to_int

"""
minutes = to_minutes(days=days, hours=hours, minutes=minutes,
seconds=seconds, milliseconds=milliseconds)
delta = timedelta(minutes= int(round(minutes)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8

return: a dictionary with the keys "days", "hours" and "minutes"
"""
tat = self.Schema().getField("MaxTimeAllowed").get(self)
return tat or self.bika_setup.getDefaultTurnaroundTime()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not allow to set a custom MaxTimeAllowed of 0. Better use return tat is not None or self.bika_setup.getDefaultTurnaroundTime()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Turnaround Time of 0 does not make sense. I mean, I would not expect the labman to want an analysis to be resolved in less than a minute since the sample was received at the lab. Thus, I though this check was not necessary and I assumed that either if MaxTimeAllowed is 0 or None, the result is the same: return the system default

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, but in code be better explicit to distinguish between None values (not set) and a user added int value w/o the assumption that it evaluates to False

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MaxTimeAllowed field is a DurationField, that inherits from RecordsField. It stores an array with a dictionary in position 0 with "days", "hours" and "minutes" as keys. So, getMaxTimeAllowed won't never return an int of 0, rather an empty array or an array with an empty dictionary in position 0. Thus, I think is better to keep this code as it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. I forgot about that these fields are record fields...

Copy link
Contributor

@ramonski ramonski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@ramonski ramonski merged commit 9d0ad32 into master Oct 1, 2018
@ramonski ramonski deleted the tat-refactor branch October 1, 2018 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Cleanup 🧹 Code cleanup and refactoring P1: Urgent
Development

Successfully merging this pull request may close these issues.

2 participants