Skip to content

Commit

Permalink
Fix conditions are not set when adding analyses manually via manage a…
Browse files Browse the repository at this point in the history
…nalyses view (#1997)

* Add (pre)conditions when adding analysis from Manage analyses view

* Sort conditions to match with same order as defined in services

* Changelog
  • Loading branch information
xispa authored May 20, 2022
1 parent 682a597 commit d1c46a2
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
2.2.0 (unreleased)
------------------

- #1997 Fix conditions not set when adding analyses via "Manage Analyses" view
- #1995 Dynamic assingment of "Owner" role for Client Contacts
- #1994 Support for dynamic assignment of Local Roles for context and principal
- #1992 Fix Generic Setup XML export/import adapters for Dexterity fields
Expand Down
2 changes: 1 addition & 1 deletion src/bika/lims/browser/analyses/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def is_analysis_conditions_edition_allowed(self, analysis_brain):
return False

# Omit analysis does not have conditions set
if not obj.getConditions():
if not obj.getConditions(empties=True):
return False

return True
Expand Down
34 changes: 34 additions & 0 deletions src/bika/lims/browser/fields/aranalysesfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,35 @@ def resolve_uid(self, result_range):
value["uid"] = uid
return value

def resolve_conditions(self, analysis):
"""Returns the conditions to be applied to this analysis by merging
those already set at sample level with defaults
"""
service = analysis.getAnalysisService()
default_conditions = service.getConditions()

# Extract the conditions set for this analysis already
existing = analysis.getConditions()
existing_titles = [cond.get("title") for cond in existing]

def is_missing(condition):
return condition.get("title") not in existing_titles

# Add only those conditions that are missing
missing = filter(is_missing, default_conditions)

# Sort them to match with same order as in service
titles = [condition.get("title") for condition in default_conditions]

def index(condition):
cond_title = condition.get("title")
if cond_title in titles:
return titles.index(cond_title)
return len(titles)

conditions = existing + missing
return sorted(conditions, key=lambda con: index(con))

def add_analysis(self, instance, service, **kwargs):
service_uid = api.get_uid(service)

Expand Down Expand Up @@ -269,6 +298,11 @@ def add_analysis(self, instance, service, **kwargs):
# Set the result range to the analysis
analysis_rr = specs.get(service_uid) or analysis.getResultsRange()
analysis.setResultsRange(analysis_rr)

# Set default (pre)conditions
conditions = self.resolve_conditions(analysis)
analysis.setConditions(conditions)

analysis.reindexObject()

def generate_analysis_id(self, instance, service):
Expand Down
34 changes: 30 additions & 4 deletions src/bika/lims/content/abstractroutineanalysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,19 +449,21 @@ def setInternalUse(self, internal_use):
else:
noLongerProvides(self, IInternalUse)

def getConditions(self):
def getConditions(self, empties=False):
"""Returns the conditions of this analysis. These conditions are usually
set on sample registration and are stored at sample level
set on sample registration and are stored at sample level. Do not return
conditions with empty value unless `empties` is True
"""
sample = self.getRequest()
service_uid = self.getRawAnalysisService()

def is_valid(condition):
uid = condition.get("uid")
if api.is_uid(uid) and uid == service_uid:
if empties:
return True
value = condition.get("value", None)
if value:
return "title" in condition
return value not in [None, ""]
return False

conditions = sample.getServiceConditions()
Expand All @@ -483,4 +485,28 @@ def setConditions(self, conditions):
# Keep the conditions from sample for analyses other than this one
other_conditions = filter(lambda c: c.get("uid") != service_uid,
sample_conditions)

def to_condition(condition):
# Type and title are required
title = condition.get("title")
cond_type = condition.get("type")
if not all([title, cond_type]):
return None

condition_info = {
"uid": service_uid,
"type": cond_type,
"title": title,
"description": "",
"choices": "",
"default": "",
"required": "",
"value": "",
}
condition = dict(condition)
condition_info.update(condition)
return condition_info

# Sanitize the conditions
conditions = filter(None, [to_condition(cond) for cond in conditions])
sample.setServiceConditions(other_conditions + conditions)
5 changes: 4 additions & 1 deletion src/senaite/core/browser/modals/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ def get_analysis_name(self):
return api.get_title(analysis)

def get_conditions(self):
conditions = self.get_analysis().getConditions()
"""Returns the conditions to display in the form, those with empty or
non-set value included
"""
conditions = self.get_analysis().getConditions(empties=True)
conditions = copy.deepcopy(conditions)
for condition in conditions:
choices = condition.get("choices", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@
tal:define="conditions python:view.get_conditions()"
tal:condition="nocall:conditions">
<div class="col-sm-12">
<h3>
<h3 class="mb-4">
<i class="fas fa-list"/>
<span i18n:translate="" tal:content="python:view.get_analysis_name()"/>
</h3>
<h4>
<span i18n:translate="">Set analysis conditions</span>
</h4>
<form name="set-analysis-conditions-form"
class="form"
method="POST"
Expand All @@ -32,7 +29,9 @@
<col style="width:20%"/>
</colgroup>
<tal:condition repeat="condition python:view.get_conditions()">
<tr tal:define="required python:condition.get('required') == 'on';">
<tr tal:define="required python:condition.get('required') == 'on';
is_checkbox python:condition.get('type') == 'checkbox';
required python:required and not is_checkbox;">
<td>
<label class="formQuestion">
<span tal:content="structure condition/title"/>
Expand Down Expand Up @@ -61,10 +60,16 @@
tal:attributes="value condition/required"/>

<select tal:condition="options"
tal:define="value condition/value|nothing"
name="conditions.value:records">
<option tal:condition="required"/>
<option tal:repeat="option options"
tal:content="option"/>
<option tal:condition="not:required"/>
<tal:option repeat="option options">
<option tal:content="option"
tal:condition="python:option == value"
selected="selected"/>
<option tal:content="option"
tal:condition="python:option != value" />
</tal:option>
</select>

<tal:non_select condition="not:options">
Expand All @@ -77,7 +82,6 @@
<input tal:attributes="type condition/type;
value condition/value"
tal:condition="not:required"
required="required"
name="conditions.value:records"/>
</tal:non_select>

Expand Down
52 changes: 52 additions & 0 deletions src/senaite/core/upgrade/v02_02_000.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ def upgrade(tool):
# Remove explicit owner role for contacts
remove_contacts_ownership(portal)

# Fix analyses conditions
fix_analyses_conditions(portal)

logger.info("{0} upgraded to version {1}".format(product, version))
return True

Expand Down Expand Up @@ -280,3 +283,52 @@ def remove_owner_role(obj, usernames):
remove_owner_role(client, usernames)

logger.info("Removing Contacts explicit ownership [DONE]")


def fix_analyses_conditions(portal):
"""Fixes the analyses conditions
"""
def to_condition(condition):
# UID, type and title are required
uid = condition.get("uid")
title = condition.get("title")
cond_type = condition.get("type")
if not all([api.is_uid(uid), title, cond_type]):
return None

condition_info = {
"uid": uid,
"title": title,
"type": cond_type,
"description": "",
"choices": "",
"default": "",
"required": "",
"value": "",
}
condition = dict(condition)
condition_info.update(condition)
return condition_info

logger.info("Reassign analyses conditions ...")
query = {
"portal_type": "Analysis",
"review_state": ["registered", "unassigned", "assigned",
"to_be_verified"]
}
brains = api.search(query, ANALYSIS_CATALOG)
for brain in brains:
analysis = api.get_object(brain)
if not IRequestAnalysis.providedBy(analysis):
continue

sample = analysis.getRequest()
conditions = sample.getServiceConditions()
if not conditions:
continue

# Sanitize and reassign the conditions
conditions = filter(None, map(to_condition, conditions))
sample.setServiceConditions(conditions)

logger.info("Reassign analyses conditions [DONE]")

0 comments on commit d1c46a2

Please sign in to comment.