Skip to content

Commit

Permalink
Change sample analysis column order for better results capturing (#2265)
Browse files Browse the repository at this point in the history
* Added collapse toggles for analyses sections

* Added configuration registry

* Changelog updated

* Sort sample analyses columns for results capturing

* Move retest right after the specification

* Changelog updated

* Allow manual override of analysis columns

* Use static list of default columns

* Only reorder sample analysis columns

* Added upgrade step

* Refactored methods to take items explicitly

* Changed registry configuration for columns config

* Changed logic to set visibility as well

* Provide default factory

* Return a copy of the default items

* Renamed visibility -> visible

* Renamed visibility -> visible

* Build back to handle just the order

---------

Co-authored-by: Jordi Puiggené <jp@naralabs.com>
  • Loading branch information
ramonski and xispa authored Mar 2, 2023
1 parent 4415269 commit cfc7188
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 43 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.4.0 (unreleased)
------------------

- #2265 Change sample analysis column order for better results capturing
- #2264 Collapsible analyses listings in sample view
- #2262 Simplify attachment render in report options to single checkbox
- #2259 Prevent string results from formatting and number conversion
Expand Down
106 changes: 64 additions & 42 deletions src/bika/lims/browser/analyses/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

from bika.lims import api
from bika.lims import bikaMessageFactory as _
from bika.lims import FieldEditAnalysisConditions
from bika.lims import logger
from bika.lims.api.analysis import get_formatted_interval
from bika.lims.api.analysis import is_out_of_range
Expand All @@ -35,10 +34,11 @@
from bika.lims.config import UDL
from bika.lims.interfaces import IAnalysisRequest
from bika.lims.interfaces import IFieldIcons
from bika.lims.interfaces import IRoutineAnalysis
from bika.lims.interfaces import IReferenceAnalysis
from bika.lims.interfaces import IRoutineAnalysis
from bika.lims.permissions import EditFieldResults
from bika.lims.permissions import EditResults
from bika.lims.permissions import FieldEditAnalysisConditions
from bika.lims.permissions import FieldEditAnalysisHidden
from bika.lims.permissions import FieldEditAnalysisResult
from bika.lims.permissions import TransitionVerify
Expand All @@ -59,6 +59,7 @@
from senaite.app.listing import ListingView
from senaite.core.catalog import ANALYSIS_CATALOG
from senaite.core.catalog import SETUP_CATALOG
from senaite.core.registry import get_registry_record
from zope.component import getAdapters
from zope.component import getMultiAdapter

Expand Down Expand Up @@ -120,29 +121,6 @@ def __init__(self, context, request, **kwargs):
"attr": "Title",
"index": "sortable_title",
"sortable": False}),
("Method", {
"title": _("Method"),
"sortable": False,
"ajax": True,
"on_change": "_on_method_change",
"toggle": True}),
("Instrument", {
"title": _("Instrument"),
"ajax": True,
"sortable": False,
"toggle": True}),
("Calculation", {
"title": _("Calculation"),
"sortable": False,
"toggle": False}),
("Analyst", {
"title": _("Analyst"),
"sortable": False,
"ajax": True,
"toggle": True}),
("state_title", {
"title": _("Status"),
"sortable": False}),
("DetectionLimitOperand", {
"title": _("DL"),
"sortable": False,
Expand Down Expand Up @@ -172,20 +150,43 @@ def __init__(self, context, request, **kwargs):
"title": _("Retested"),
"type": "boolean",
"sortable": False}),
("Method", {
"title": _("Method"),
"sortable": False,
"ajax": True,
"on_change": "_on_method_change",
"toggle": True}),
("Instrument", {
"title": _("Instrument"),
"ajax": True,
"sortable": False,
"toggle": True}),
("Calculation", {
"title": _("Calculation"),
"sortable": False,
"toggle": False}),
("Attachments", {
"title": _("Attachments"),
"sortable": False}),
("SubmittedBy", {
"title": _("Submitter"),
"sortable": False}),
("Analyst", {
"title": _("Analyst"),
"sortable": False,
"ajax": True,
"toggle": True}),
("CaptureDate", {
"title": _("Captured"),
"index": "getResultCaptureDate",
"sortable": False}),
("SubmittedBy", {
"title": _("Submitter"),
"sortable": False}),
("DueDate", {
"title": _("Due Date"),
"index": "getDueDate",
"sortable": False}),
("state_title", {
"title": _("Status"),
"sortable": False}),
("Hidden", {
"title": _("Hidden"),
"toggle": True,
Expand Down Expand Up @@ -255,6 +256,32 @@ def before_render(self):
super(AnalysesView, self).before_render()
self.request.set("disable_plone.rightcolumn", 1)

@viewcache.memoize
def get_default_columns_order(self):
"""Return the default column order from the registry
:returns: List of column keys
"""
name = "sampleview_analysis_columns_order"
return get_registry_record(name, default=[])

def reorder_analysis_columns(self):
"""Reorder analysis columns based on registry configuration
"""
columns_order = self.get_default_columns_order()
if not columns_order:
return
# compute columns that are missing in the config
missing_columns = filter(
lambda col: col not in columns_order, self.columns.keys())
# prepare the new sort order for the columns
ordered_columns = columns_order + missing_columns

# set the order in each review state
for rs in self.review_states:
# set a copy of the new ordered columns list
rs["columns"] = ordered_columns[:]

@property
@viewcache.memoize
def senaite_theme(self):
Expand Down Expand Up @@ -658,7 +685,6 @@ def folderitem(self, obj, item, index):
item['Keyword'] = obj.getKeyword
item['Unit'] = format_supsub(obj.getUnit) if obj.getUnit else ''
item['retested'] = obj.getRetestOfUID and True or False
item['class']['retested'] = 'center'
item['replace']['Service'] = '<strong>{}</strong>'.format(obj.Title)

# Append info link before the service
Expand Down Expand Up @@ -789,9 +815,6 @@ def folderitems(self):
pos = "Result" in state["columns"] and \
state["columns"].index("Uncertainty") + 1 or len(
state["columns"])
if "retested" in state["columns"]:
state["columns"].remove("retested")
state["columns"].insert(pos, "retested")
new_states.append(state)
self.review_states = new_states
# Allow selecting individual analyses
Expand All @@ -805,20 +828,19 @@ def folderitems(self):

# self.json_specs = json.dumps(self.specs)
self.json_interim_fields = json.dumps(self.interim_fields)
self.items = items

# Display method and instrument columns only if at least one of the
# analyses requires them to be displayed for selection
show_method_column = self.is_method_column_required()
show_method_column = self.is_method_column_required(items)
if "Method" in self.columns:
self.columns["Method"]["toggle"] = show_method_column

show_instrument_column = self.is_instrument_column_required()
show_instrument_column = self.is_instrument_column_required(items)
if "Instrument" in self.columns:
self.columns["Instrument"]["toggle"] = show_instrument_column

# show unit selection column only if required
show_unit_column = self.is_unit_selection_column_required()
show_unit_column = self.is_unit_selection_column_required(items)
if "Unit" in self.columns:
self.columns["Unit"]["toggle"] = show_unit_column

Expand Down Expand Up @@ -1661,34 +1683,34 @@ def is_unit_choices_required(self, analysis):
return True
return False

def is_method_column_required(self):
def is_method_column_required(self, items):
"""Returns whether the method column has to be rendered or not.
Returns True if at least one of the analyses from the listing requires
the list for method selection to be rendered
"""
for item in self.items:
for item in items:
obj = item.get("obj")
if self.is_method_required(obj):
return True
return False

def is_instrument_column_required(self):
def is_instrument_column_required(self, items):
"""Returns whether the instrument column has to be rendered or not.
Returns True if at least one of the analyses from the listing requires
the list for instrument selection to be rendered
"""
for item in self.items:
for item in items:
obj = item.get("obj")
if self.is_instrument_required(obj):
return True
return False

def is_unit_selection_column_required(self):
def is_unit_selection_column_required(self, items):
"""Returns whether the unit column has to be rendered or not.
Returns True if at least one of the analyses from the listing requires
the list for unit selection to be rendered
"""
for item in self.items:
for item in items:
obj = item.get("obj")
if self.is_unit_choices_required(obj):
return True
Expand Down
3 changes: 3 additions & 0 deletions src/bika/lims/browser/analysisrequest/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(self, context, request):
self.show_workflow_action_buttons = True
self.show_select_column = True
self.show_search = False
self.reorder_analysis_columns()


class FieldAnalysesTable(AnalysesView):
Expand All @@ -59,6 +60,7 @@ def __init__(self, context, request):
self.show_workflow_action_buttons = True
self.show_select_column = True
self.show_search = False
self.reorder_analysis_columns()


class QCAnalysesTable(QCAnalysesView):
Expand All @@ -73,3 +75,4 @@ def __init__(self, context, request):
self.show_select_column = False
self.show_workflow_action_buttons = False
self.show_search = False
self.reorder_analysis_columns()
2 changes: 1 addition & 1 deletion src/senaite/core/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<metadata>
<version>2421</version>
<version>2422</version>
<dependencies>
<dependency>profile-Products.ATContentTypes:base</dependency>
<dependency>profile-Products.CMFEditions:CMFEditions</dependency>
Expand Down
30 changes: 30 additions & 0 deletions src/senaite/core/registry/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ISampleViewRegistry(ISenaiteRegistry):
"sampleview_collapse_field_analysis_table",
"sampleview_collapse_lab_analysis_table",
"sampleview_collapse_qc_analysis_table",
"sampleview_analysis_columns_order",
],
)
sampleview_collapse_field_analysis_table = schema.Bool(
Expand All @@ -44,6 +45,35 @@ class ISampleViewRegistry(ISenaiteRegistry):
required=False,
)

sampleview_analysis_columns_order = schema.List(
title=_(u"Analysis columns order"),
description=_(
u"Default column order for sample analysis listings"
),
value_type=schema.ASCIILine(title=u"Column"),
required=False,
default=[
"created",
"Service",
"DetectionLimitOperand",
"Result",
"Uncertainty",
"Unit",
"Specification",
"retested",
"Method",
"Instrument",
"Calculation",
"Attachments",
"SubmittedBy",
"Analyst",
"CaptureDate",
"DueDate",
"state_title",
"Hidden",
]
)


class ISampleHeaderRegistry(ISenaiteRegistry):
"""Registry settings for sample header configuration
Expand Down
8 changes: 8 additions & 0 deletions src/senaite/core/upgrade/v02_04_000.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,12 @@
handler="senaite.core.upgrade.v02_04_000.import_registry"
profile="senaite.core:default"/>

<genericsetup:upgradeStep
title="SENAITE CORE 2.4.0: Update configuration registry"
description="Update configuration registry for sample analyses columns config"
source="2421"
destination="2422"
handler="senaite.core.upgrade.v02_04_000.import_registry"
profile="senaite.core:default"/>

</configure>

0 comments on commit cfc7188

Please sign in to comment.