Skip to content

Commit

Permalink
Settings for notification and automatic creation of missing analyses …
Browse files Browse the repository at this point in the history
…at referring lab (#27)

* Add notify_all_analyses setting

* Add setting for automatic creation of missing tests at referring laboratory

* Use get instead of pop

* Better description for upgrade step
  • Loading branch information
xispa authored Jul 22, 2024
1 parent 40d706b commit 75b34ee
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 19 deletions.
34 changes: 34 additions & 0 deletions src/senaite/referral/browser/controlpanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,40 @@ class IReferralControlPanel(Interface):
required=0,
)

notify_all_analyses = schema.Bool(
title=_(
u"label_referral_notify_all_analyses",
u"Notify all analyses to referring laboratory"
),
description=_(
u"description_referral_notify_all_analyses",
u"If selected, the system will send all analyses back to the "
u"referring laboratory for results update after verification, "
u"those that weren't requested through the shipment included. "
u"Otherwise, the system will send notifications back only for "
u"those analyses that were initially requested."
),
default=False,
required=False,
)

create_reference_analyses = schema.Bool(
title=_(
u"label_referral_create_reference_analyses",
u"Create analyses from reference laboratory"
),
description=_(
u"description_referral_create_reference_analyses",
u"If selected, the system will create missing analyses in the "
u"referring laboratory when receiving a notification for results "
u"update from the reference laboratory. Otherwise, the system "
u"will skip results for analyses that do not exist in the "
u"referred sample."
),
default=False,
required=False,
)


class ReferralControlPanelForm(RegistryEditForm):
schema = IReferralControlPanel
Expand Down
37 changes: 31 additions & 6 deletions src/senaite/referral/jsonapi/outboundsample.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@

from senaite.jsonapi.exceptions import APIError
from senaite.jsonapi.interfaces import IPushConsumer
from senaite.referral.utils import get_create_reference_analyses
from senaite.referral.utils import get_services_mapping
from zope.interface import alsoProvides
from zope.interface import implementer

from bika.lims import api
from bika.lims.catalog import CATALOG_ANALYSIS_REQUEST_LISTING
from bika.lims.interfaces import ISubmitted
from bika.lims.utils import changeWorkflowState
from bika.lims.utils.analysis import create_analysis
from bika.lims.workflow import doActionFor
from senaite.referral import logger

Expand Down Expand Up @@ -83,15 +86,37 @@ def process(self):

# TODO Performance - convert to queue task

# Get the analyses that are in a suitable status grouped by keyword
statuses = ["referred", "assigned", "unassigned"]
by_keyword = self.get_analyses_by_keyword(sample, statuses)
# Get the analyses grouped by keyword
by_keyword = self.get_analyses_by_keyword(sample)

# Allowed statuses
statuses = dict.fromkeys(["referred", "assigned", "unassigned"], True)

# Do we need to create non-existing analyses
create_missing = get_create_reference_analyses()
services = get_services_mapping() if create_missing else {}

# Update the analyses passed-in
analysis_records = sample_record.get("analyses")
for analysis_record in analysis_records:
keyword = analysis_record.get("keyword")
for analysis in by_keyword.get(keyword):

# pop the analyses to be updated for the given keyword
analyses = by_keyword.get(keyword, [])
if not analyses:
service_uid = services.get(keyword)
service = api.get_object(service_uid, default=None)
if service:
# create the missing analysis
analyses = [create_analysis(sample, service)]

for analysis in analyses:
# skip if status is not valid
status = api.get_review_status(analysis)
if not statuses.get(status, False):
continue

# update the analysis
try:
self.update_analysis(analysis, analysis_record)
except Exception as e:
Expand All @@ -100,11 +125,11 @@ def process(self):

return True

def get_analyses_by_keyword(self, sample, statuses):
def get_analyses_by_keyword(self, sample):
"""Returns the analyses of the sample grouped by keyword
"""
groups = {}
analyses = sample.getAnalyses(full_objects=True, review_state=statuses)
analyses = sample.getAnalyses(full_objects=True)
for analysis in analyses:
keyword = analysis.getKeyword()
groups.setdefault(keyword, []).append(analysis)
Expand Down
2 changes: 1 addition & 1 deletion src/senaite/referral/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
dependencies before installing this add-on own profile.
-->
<metadata>
<version>1008</version>
<version>1009</version>

<!-- Be sure to install the following dependencies if not yet installed -->
<dependencies>
Expand Down
25 changes: 13 additions & 12 deletions src/senaite/referral/remotelab.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,22 @@
# Some rights reserved, see README and LICENSE.

import math

from remotesession import RemoteSession
from requests.auth import HTTPBasicAuth
from senaite.core.supermodel import SuperModel
from senaite.referral import logger
from senaite.referral.interfaces import IExternalLaboratory
from senaite.referral.notifications import get_post_base_info
from senaite.referral.notifications import save_post
from senaite.referral.utils import get_lab_code
from senaite.referral.utils import get_notify_all_analyses
from senaite.referral.utils import get_user_info
from senaite.referral.utils import is_valid_url

from bika.lims import api
from bika.lims.interfaces import IAnalysisRequest
from bika.lims.interfaces import IInternalUse
from bika.lims.utils import format_supsub
from bika.lims.utils.analysis import format_uncertainty
from remotesession import RemoteSession


def get_remote_connection(laboratory):
Expand Down Expand Up @@ -191,30 +190,32 @@ def update_analyses(self, sample, timeout=5):
"""

def get_valid_analyses(sample):
# Get the analyses from current sample that were requested by the
# referring laboratory, sorted by id descending to prioritize
# newest results if retests
inbound_sample = sample.getInboundSample()
services = inbound_sample.getRawServices()
kwargs = {
# Get the analyses to notify about to the reference laboratory,
# sorted by id descending to prioritize newest results if retests
query = {
"full_objects": True,
"getServiceUID": services,
"sort_on": "id",
"sort_order": "ascending",
}

notify_all = get_notify_all_analyses()
if not notify_all:
# only notify about analyses that were requested via shipment
inbound_sample = sample.getInboundSample()
query["getServiceUID"] = inbound_sample.getRawServices()

# exclude old, but valid analyses with same keyword (e.g retests),
# cause we want to update the referring lab with the newest result
analyses = {}
valid = ["verified", "published"]
for analysis in sample.getAnalyses(**kwargs):
for analysis in sample.getAnalyses(**query):

# Skip analyses not in a suitable status
if api.get_review_status(analysis) not in valid:
continue

# Skip retested, only interested in final results
if analysis.getRetest():
if analysis.getRawRetest():
continue

keyword = analysis.getKeyword()
Expand Down
8 changes: 8 additions & 0 deletions src/senaite/referral/upgrade/v01_00_000.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,11 @@ def setup_inbound_services(tool):
obj._p_deactivate()

logger.info("Setup inbound services [DONE]")


def setup_results_notification(tool):
logger.info("Setup results notification settings ...")
portal = tool.aq_inner.aq_parent
setup = portal.portal_setup
setup.runImportStepFromProfile(profile, "plone.app.registry")
logger.info("Setup results notification settings [DONE]")
8 changes: 8 additions & 0 deletions src/senaite/referral/upgrade/v01_00_000.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">

<genericsetup:upgradeStep
title="SENAITE.REFERRAL 1.0.0: Setup results notification settings"
description="Setup results notification settings"
source="1008"
destination="1009"
handler=".v01_00_000.setup_results_notification"
profile="senaite.referral:default"/>

<genericsetup:upgradeStep
title="SENAITE.REFERRAL 1.0.0: Store service uids in inbound sample"
description="Store service uids in inbound sample"
Expand Down
16 changes: 16 additions & 0 deletions src/senaite/referral/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,19 @@ def get_services_mapping():
services[keyword] = uid
services[uid] = uid
return services


def get_notify_all_analyses():
"""Returns whether the system has to send notifications for analyses that
weren't initially requested
"""
key = "{}.notify_all_analyses".format(PRODUCT_NAME)
return api.get_registry_record(key, default=False)


def get_create_reference_analyses():
"""Returns whether the system has to create analyses if results are
notified by reference lab, but the sample does not have them
"""
key = "{}.create_reference_analyses".format(PRODUCT_NAME)
return api.get_registry_record(key, default=False)

0 comments on commit 75b34ee

Please sign in to comment.