diff --git a/src/senaite/referral/browser/controlpanel.py b/src/senaite/referral/browser/controlpanel.py index d6c0d97..b2d0090 100644 --- a/src/senaite/referral/browser/controlpanel.py +++ b/src/senaite/referral/browser/controlpanel.py @@ -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 diff --git a/src/senaite/referral/jsonapi/outboundsample.py b/src/senaite/referral/jsonapi/outboundsample.py index debd866..ca3a233 100644 --- a/src/senaite/referral/jsonapi/outboundsample.py +++ b/src/senaite/referral/jsonapi/outboundsample.py @@ -25,6 +25,8 @@ from senaite.core.workflow import ANALYSIS_WORKFLOW 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 @@ -32,6 +34,7 @@ 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 @@ -84,15 +87,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: @@ -101,11 +126,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) diff --git a/src/senaite/referral/profiles/default/metadata.xml b/src/senaite/referral/profiles/default/metadata.xml index de9472e..b207024 100644 --- a/src/senaite/referral/profiles/default/metadata.xml +++ b/src/senaite/referral/profiles/default/metadata.xml @@ -6,7 +6,7 @@ dependencies before installing this add-on own profile. --> - 2005 + 2006 diff --git a/src/senaite/referral/remotelab.py b/src/senaite/referral/remotelab.py index 59624b8..1541169 100644 --- a/src/senaite/referral/remotelab.py +++ b/src/senaite/referral/remotelab.py @@ -23,7 +23,6 @@ from bika.lims.interfaces import IAnalysisRequest from bika.lims.utils import format_supsub from bika.lims.utils.analysis import format_uncertainty -from remotesession import RemoteSession from requests.auth import HTTPBasicAuth from senaite.app.supermodel import SuperModel from senaite.core.api.dtime import date_to_string @@ -31,7 +30,9 @@ 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.remotesession import RemoteSession 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 @@ -190,30 +191,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() diff --git a/src/senaite/referral/upgrade/v02_00_000.py b/src/senaite/referral/upgrade/v02_00_000.py index a0e5fc2..68bb59b 100644 --- a/src/senaite/referral/upgrade/v02_00_000.py +++ b/src/senaite/referral/upgrade/v02_00_000.py @@ -161,3 +161,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]") diff --git a/src/senaite/referral/upgrade/v02_00_000.zcml b/src/senaite/referral/upgrade/v02_00_000.zcml index ce5b7c4..54b6ae1 100644 --- a/src/senaite/referral/upgrade/v02_00_000.zcml +++ b/src/senaite/referral/upgrade/v02_00_000.zcml @@ -4,7 +4,15 @@ i18n_domain="senaite.referral"> + +