From ce0541047aacd2ad8d236130cf7eb2037ee29caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Thu, 31 Aug 2017 18:47:50 +0200 Subject: [PATCH 1/2] Basics to allow a single Analysis (not Service) to be hidden in results report --- bika/lims/browser/analysisrequest/publish.py | 7 +-- bika/lims/content/abstractroutineanalysis.py | 49 ++++++++++++++++++++ bika/lims/utils/analysis.py | 2 +- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/bika/lims/browser/analysisrequest/publish.py b/bika/lims/browser/analysisrequest/publish.py index 9b9cb71b1b..2f3be2eb91 100644 --- a/bika/lims/browser/analysisrequest/publish.py +++ b/bika/lims/browser/analysisrequest/publish.py @@ -1189,11 +1189,8 @@ def _analyses_data(self, ar, analysis_states=None): full_objects=True, review_state=analysis_states): # Omit hidden analyses? - if not showhidden: - asets = ar.getAnalysisServiceSettings(an.getServiceUID()) - if asets.get('hidden'): - # Hide analysis - continue + if not showhidden and an.getHidden(): + continue # Build the analysis-specific dict andict = self._analysis_data(an, dm) diff --git a/bika/lims/content/abstractroutineanalysis.py b/bika/lims/content/abstractroutineanalysis.py index d08c537678..fe946c8aa4 100644 --- a/bika/lims/content/abstractroutineanalysis.py +++ b/bika/lims/content/abstractroutineanalysis.py @@ -88,6 +88,16 @@ label=_("Uncertainty") ) ) +# This field keep track if the field hidden has been set manually or not. If +# this value is false, the system will assume the visibility of this analysis +# in results report will depend on the value set at AR, Profile or Template +# levels (see AnalysisServiceSettings fields in AR). If the value for this +# field is set to true, the system will assume the visibility of the analysis +# will only depend on the value set for the field Hidden (bool). +HiddenManually = BooleanField( + 'HiddenManually', + default=False, +) schema = schema.copy() + Schema(( IsReflexAnalysis, @@ -98,6 +108,7 @@ ReflexRuleLocalID, SamplePartition, Uncertainty, + HiddenManually, )) @@ -424,6 +435,44 @@ def getPrioritySortkey(self): if analysis_request: return analysis_request.getPrioritySortkey() + @security.public + def getHidden(self): + """ Returns whether if the analysis must be displayed in results + reports or not, as well as in analyses view when the user logged in + is a Client Contact. + + If the value for the field HiddenManually is set to False, this function + will delegate the action to the method getAnalysisServiceSettings() from + the Analysis Request. + + If the value for the field HiddenManually is set to True, this function + will return the value of the field Hidden. + :return: true or false + """ + if self.getHiddenManually(): + return self.getSchema().getField('Hidden').get(self) + request = self.getRequest() + if request: + service_uid = self.getServiceUID() + ar_settings = request.getAnalysisServiceSettings(service_uid) + return ar_settings.get('hidden', False) + return False + + @security.public + def setHidden(self, hidden): + """ Sets if this analysis must be displayed or not in results report and + in manage analyses view if the user is a lab contact as well. + + The value set by using this field will have priority over the visibility + criteria set at Analysis Request, Template or Profile levels (see + field AnalysisServiceSettings from Analysis Request. To achieve this + behavior, this setter also sets the value to HiddenManually to true. + :param hidden: true if the analysis must be hidden in results + :rtype hidden: bool + """ + self.setHiddenManually(True) + self.Schema().getField('Hidden').set(self, hidden) + @security.public def setReflexAnalysisOf(self, analysis): """Sets the analysis that has been reflexed in order to create this diff --git a/bika/lims/utils/analysis.py b/bika/lims/utils/analysis.py index 40ed245f53..8bf9288e5a 100644 --- a/bika/lims/utils/analysis.py +++ b/bika/lims/utils/analysis.py @@ -50,7 +50,7 @@ def copy_analysis_field_values(source, analysis, **kwargs): 'creators', 'effectiveDate', 'expirationDate', 'language', 'rights', 'creation_date', 'modification_date', 'IsReflexAnalysis', 'OriginalReflexedAnalysis', 'ReflexAnalysisOf', 'ReflexRuleAction', - 'ReflexRuleLocalID', 'ReflexRuleActionsTriggered'] + 'ReflexRuleLocalID', 'ReflexRuleActionsTriggered', 'Hidden'] for field in src_schema.fields(): fieldname = field.getName() if fieldname in IGNORE_FIELDNAMES and fieldname not in kwargs: From e6177a059c6c69e22f3bd264bf61a6cbc00360e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 1 Sep 2017 01:28:48 +0200 Subject: [PATCH 2/2] Allow to set an analysis as hidden dynamically in Analyses View --- bika/lims/browser/analyses.py | 38 ++++++++++++++++++- bika/lims/browser/bika_listing.py | 8 ++++ bika/lims/browser/js/bika.lims.bikalisting.js | 14 ++++--- bika/lims/content/abstractroutineanalysis.py | 9 +++-- bika/lims/skins/bika/ploneCustom.css.dtml | 10 +++-- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/bika/lims/browser/analyses.py b/bika/lims/browser/analyses.py index c77253907b..5994a0c8db 100644 --- a/bika/lims/browser/analyses.py +++ b/bika/lims/browser/analyses.py @@ -14,6 +14,7 @@ from bika.lims.browser.bika_listing import BikaListingView from bika.lims.config import QCANALYSIS_TYPES from bika.lims.interfaces import IResultOutOfRange +from bika.lims.interfaces import IRoutineAnalysis from bika.lims.permissions import * from bika.lims.permissions import Verify as VerifyPermission from bika.lims.utils import isActive @@ -137,6 +138,12 @@ def __init__(self, context, request, **kwargs): 'title': _('Due Date'), 'index': 'getDueDate', 'sortable': False}, + 'Hidden': { + 'title': _('Hidden'), + 'toggle': True, + 'sortable': False, + 'input_class': 'autosave', + 'type': 'boolean'}, } self.review_states = [ @@ -154,7 +161,8 @@ def __init__(self, context, request, **kwargs): 'Uncertainty', 'CaptureDate', 'DueDate', - 'state_title'] + 'state_title', + 'Hidden'] }, ] if not context.bika_setup.getShowPartitions(): @@ -914,6 +922,23 @@ def folderitem(self, obj, item, index): t(_('It comes form a reflex rule')) )) item['after']['Service'] = ' '.join(after_icons) + + + # Users that can Add Analyses to an Analysis Request must be able to + # set the visibility of the analysis in results report, also if the + # current state of the Analysis Request (e.g. verified) does not allow + # the edition of other fields. Note that an analyst has no privileges + # by default to edit this value, cause this "visibility" field is + # related with results reporting and/or visibility from the client side. + # This behavior only applies to routine analyses, the visibility of QC + # analyses is managed in publish and are not visible to clients. + if 'Hidden' in self.columns: + # TODO Performance. Use brain instead + full_obj = full_obj if full_obj else obj.getObject() + item['Hidden'] = full_obj.getHidden() + if IRoutineAnalysis.providedBy(full_obj): + item['allow_edit'].append('Hidden') + return item def folderitems(self): @@ -944,6 +969,17 @@ def folderitems(self): self.allow_edit = can_edit_analyses self.show_select_column = self.allow_edit + # Users that can Add Analyses to an Analysis Request must be able to + # set the visibility of the analysis in results report, also if the + # current state of the Analysis Request (e.g. verified) does not allow + # the edition of other fields. Note that an analyst has no privileges + # by default to edit this value, cause this "visibility" field is + # related with results reporting and/or visibility from the client side. + # This behavior only applies to routine analyses, the visibility of QC + # analyses is managed in publish and are not visible to clients. + if not self.mtool.checkPermission(AddAnalysis, self.context): + self.remove_column('Hidden') + self.categories = [] # Getting the multi-verification type of bika_setup self.mv_type = self.context.bika_setup.getTypeOfmultiVerification() diff --git a/bika/lims/browser/bika_listing.py b/bika/lims/browser/bika_listing.py index d38b75bccd..1f36fb8179 100644 --- a/bika/lims/browser/bika_listing.py +++ b/bika/lims/browser/bika_listing.py @@ -813,6 +813,14 @@ def before_render(self): """ pass + def remove_column(self, column): + """Removes the column passed-in, if exists""" + if column in self.columns: + del self.columns[column] + for item in self.review_states: + if column in item.get('columns', []): + item['columns'].remove(column) + def __call__(self): """ Handle request parameters and render the form.""" diff --git a/bika/lims/browser/js/bika.lims.bikalisting.js b/bika/lims/browser/js/bika.lims.bikalisting.js index 67f800fbe3..cff0c6f7b6 100644 --- a/bika/lims/browser/js/bika.lims.bikalisting.js +++ b/bika/lims/browser/js/bika.lims.bikalisting.js @@ -674,6 +674,9 @@ function BikaListingTableView() { */ var fieldvalue, fieldname, requestdata={}, uid, tr; fieldvalue = $(pointer).val(); + if ($(pointer).is(':checkbox')) { + fieldvalue = $(pointer).is(':checked'); + } fieldname = $(pointer).attr('field'); tr = $(pointer).closest('tr'); uid = $(pointer).attr('uid'); @@ -689,7 +692,6 @@ function BikaListingTableView() { var url = window.location.href.replace('/base_view', ''); // Staff for the notification var name = $(tr).attr('title'); - var anch = "" + name + ""; $.ajax({ type: "POST", url: window.portal_url+"/@@API/update", @@ -698,18 +700,18 @@ function BikaListingTableView() { .done(function(data) { //success alert if (data != null && data['success'] == true) { - bika.lims.SiteView.notificationPanel(anch + ': ' + name + ' updated successfully', "succeed"); + bika.lims.SiteView.notificationPanel(name + ' updated successfully', "succeed"); } else { - bika.lims.SiteView.notificationPanel('Error while updating ' + name + ' for '+ anch, "error"); - var msg = '[bika.lims.analysisrequest.js] Error while updating ' + name + ' for '+ ar; + bika.lims.SiteView.notificationPanel('Error while updating ' + name, "error"); + var msg = 'Error while updating ' + name; console.warn(msg); window.bika.lims.error(msg); } }) .fail(function(){ //error - bika.lims.SiteView.notificationPanel('Error while updating ' + name + ' for '+ anch, "error"); - var msg = '[bika.lims.analysisrequest.js] Error while updating ' + name + ' for '+ ar; + bika.lims.SiteView.notificationPanel('Error while updating ' + name, "error"); + var msg = 'Error while updating ' + name; console.warn(msg); window.bika.lims.error(msg); }); diff --git a/bika/lims/content/abstractroutineanalysis.py b/bika/lims/content/abstractroutineanalysis.py index fe946c8aa4..24c1465cb9 100644 --- a/bika/lims/content/abstractroutineanalysis.py +++ b/bika/lims/content/abstractroutineanalysis.py @@ -448,9 +448,10 @@ def getHidden(self): If the value for the field HiddenManually is set to True, this function will return the value of the field Hidden. :return: true or false + :rtype: bool """ if self.getHiddenManually(): - return self.getSchema().getField('Hidden').get(self) + return self.getField('Hidden').get(self) request = self.getRequest() if request: service_uid = self.getServiceUID() @@ -467,11 +468,11 @@ def setHidden(self, hidden): criteria set at Analysis Request, Template or Profile levels (see field AnalysisServiceSettings from Analysis Request. To achieve this behavior, this setter also sets the value to HiddenManually to true. - :param hidden: true if the analysis must be hidden in results - :rtype hidden: bool + :param hidden: true if the analysis must be hidden in report + :type hidden: bool """ self.setHiddenManually(True) - self.Schema().getField('Hidden').set(self, hidden) + self.getField('Hidden').set(self, hidden) @security.public def setReflexAnalysisOf(self, analysis): diff --git a/bika/lims/skins/bika/ploneCustom.css.dtml b/bika/lims/skins/bika/ploneCustom.css.dtml index 42d1f6da5d..9d066743dd 100644 --- a/bika/lims/skins/bika/ploneCustom.css.dtml +++ b/bika/lims/skins/bika/ploneCustom.css.dtml @@ -1031,11 +1031,13 @@ h2 a.add-button { padding: 5px 20px 20px 70px; } div#viewlet-above-content-title #panel-notification { - left: 0; - margin: 100px; - padding: 0 280px; - position: absolute; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); z-index: 1000; + border: 1px solid #efefef; + box-shadow: 1px 1px 10px #fff; } div#viewlet-above-content-title #panel-notification div.error-notification-item { background: url("/Plone/++resource++bika.lims.images/warning.png") no-repeat scroll 15px center #fff680;