From 4b203e2c78b83dbc2b05ba56b23168bba2a35aa6 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Thu, 21 Dec 2017 17:25:03 +0100 Subject: [PATCH 1/5] PEP8 only --- bika/lims/content/analysisspec.py | 215 +++++++++++++++++------------- 1 file changed, 122 insertions(+), 93 deletions(-) diff --git a/bika/lims/content/analysisspec.py b/bika/lims/content/analysisspec.py index cddfdb159c..11e0d01ba2 100644 --- a/bika/lims/content/analysisspec.py +++ b/bika/lims/content/analysisspec.py @@ -1,31 +1,31 @@ # This file is part of Bika LIMS # -# Copyright 2011-2016 by it's authors. +# Copyright 2011-2017 by it's authors. # Some rights reserved. See LICENSE.txt, AUTHORS.txt. -"""Analysis result range specifications for a client -""" from AccessControl import ClassSecurityInfo - -from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin -from Products.ATExtensions.field.records import RecordsField -from Products.Archetypes import atapi -from Products.Archetypes.config import REFERENCE_CATALOG -from Products.Archetypes.public import * -from Products.Archetypes.utils import DisplayList -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import safe_unicode from bika.lims import bikaMessageFactory as _ -from bika.lims import deprecated from bika.lims.browser.fields import UIDReferenceField from bika.lims.browser.widgets import AnalysisSpecificationWidget from bika.lims.config import PROJECTNAME from bika.lims.content.bikaschema import BikaSchema from bika.lims.interfaces import IAnalysisSpec +from Products.Archetypes import atapi +from Products.Archetypes.config import REFERENCE_CATALOG +from Products.Archetypes.public import (BaseFolder, ComputedField, + ComputedWidget, ReferenceWidget, + Schema) +from Products.Archetypes.utils import DisplayList +from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin +from Products.ATExtensions.field.records import RecordsField +from Products.CMFCore.utils import getToolByName +from Products.CMFPlone.utils import safe_unicode from zope.i18n import translate from zope.interface import implements + schema = Schema(( + UIDReferenceField( 'SampleType', vocabulary="getSampleTypes", @@ -35,71 +35,94 @@ label=_("Sample Type"), ), ), - ComputedField('SampleTypeTitle', - expression = "context.getSampleType().Title() if context.getSampleType() else ''", - widget = ComputedWidget( - visible = False, + + ComputedField( + 'SampleTypeTitle', + expression="context.getSampleType().Title() if context.getSampleType() else ''", + widget=ComputedWidget( + visible=False, ), ), - ComputedField('SampleTypeUID', - expression = "context.getSampleType().UID() if context.getSampleType() else ''", - widget = ComputedWidget( - visible = False, + + ComputedField( + 'SampleTypeUID', + expression="context.getSampleType().UID() if context.getSampleType() else ''", + widget=ComputedWidget( + visible=False, ), ), -)) + \ -BikaSchema.copy() + \ -Schema(( - RecordsField('ResultsRange', +)) + BikaSchema.copy() + Schema(( + + RecordsField( + 'ResultsRange', # schemata = 'Specifications', - required = 1, - type = 'resultsrange', - subfields = ('keyword', 'min', 'max', 'error', 'hidemin', 'hidemax', - 'rangecomment'), - required_subfields = ('keyword', 'error'), - subfield_validators = {'min':'analysisspecs_validator', - 'max':'analysisspecs_validator', - 'error':'analysisspecs_validator',}, - subfield_labels = {'keyword': _('Analysis Service'), - 'min': _('Min'), - 'max': _('Max'), - 'error': _('% Error'), - 'hidemin': _('< Min'), - 'hidemax': _('> Max'), - 'rangecomment': _('Range Comment')}, - widget = AnalysisSpecificationWidget( - checkbox_bound = 0, - label = _("Specifications"), - description = _("Click on Analysis Categories (against shaded background" \ - "to see Analysis Services in each category. Enter minimum " \ - "and maximum values to indicate a valid results range. " \ - "Any result outside this range will raise an alert. " \ - "The % Error field allows for an % uncertainty to be " \ - "considered when evaluating results against minimum and " \ - "maximum values. A result out of range but still in range " \ - "if the % error is taken into consideration, will raise a " \ - "less severe alert. If the result is below '< Min' " \ - "the result will be shown as '< [min]'. The same " \ - "applies for results above '> Max'"), + required=1, + type='resultsrange', + subfields=( + 'keyword', + 'min', + 'max', + 'error', + 'hidemin', + 'hidemax', + 'rangecomment' + ), + required_subfields=('keyword', 'error'), + subfield_validators={ + 'min': 'analysisspecs_validator', + 'max': 'analysisspecs_validator', + 'error': 'analysisspecs_validator', + }, + subfield_labels={ + 'keyword': _('Analysis Service'), + 'min': _('Min'), + 'max': _('Max'), + 'error': _('% Error'), + 'hidemin': _('< Min'), + 'hidemax': _('> Max'), + 'rangecomment': _('Range Comment'), + }, + widget=AnalysisSpecificationWidget( + checkbox_bound=0, + label=_("Specifications"), + description=_( + "Click on Analysis Categories (against shaded background" \ + "to see Analysis Services in each category. Enter minimum " \ + "and maximum values to indicate a valid results range. " \ + "Any result outside this range will raise an alert. " \ + "The % Error field allows for an % uncertainty to be " \ + "considered when evaluating results against minimum and " \ + "maximum values. A result out of range but still in range " \ + "if the % error is taken into consideration, will raise a " \ + "less severe alert. If the result is below '< Min' " \ + "the result will be shown as '< [min]'. The same " \ + "applies for results above '> Max'"), ), ), - ComputedField('ClientUID', - expression = "context.aq_parent.UID()", - widget = ComputedWidget( - visible = False, + + ComputedField( + 'ClientUID', + expression="context.aq_parent.UID()", + widget=ComputedWidget( + visible=False, ), ), )) + schema['description'].widget.visible = True schema['title'].required = True + class AnalysisSpec(BaseFolder, HistoryAwareMixin): + """Analysis Specification + """ implements(IAnalysisSpec) security = ClassSecurityInfo() schema = schema displayContentsTab = False _at_rename_after_creation = True + def _renameAfterCreation(self, check_auto_id=False): from bika.lims.idserver import renameAfterCreation renameAfterCreation(self) @@ -121,33 +144,34 @@ def Title(self): def contextual_title(self): parent = self.aq_parent if parent == self.bika_setup.bika_analysisspecs: - return self.title + " ("+translate(_("Lab"))+")" + return self.title + " (" + translate(_("Lab")) + ")" else: - return self.title + " ("+translate(_("Client"))+")" + return self.title + " (" + translate(_("Client")) + ")" security.declarePublic('getSpecCategories') + def getSpecCategories(self): bsc = getToolByName(self, 'bika_setup_catalog') categories = [] for spec in self.getResultsRange(): keyword = spec['keyword'] service = bsc(portal_type="AnalysisService", - getKeyword = keyword) + getKeyword=keyword) if service.getCategoryUID() not in categories: categories.append(service.getCategoryUID()) return categories security.declarePublic('getResultsRangeDict') + def getResultsRangeDict(self): - """ - Return a dictionary with the specification fields for each - service. The keys of the dictionary are the keywords of each - analysis service. Each service contains a dictionary in which - each key is the name of the spec field: - specs['keyword'] = {'min': value, - 'max': value, - 'error': value, - ... } + """Return a dictionary with the specification fields for each + service. The keys of the dictionary are the keywords of each + analysis service. Each service contains a dictionary in which + each key is the name of the spec field: + specs['keyword'] = {'min': value, + 'max': value, + 'error': value, + ... } """ specs = {} subfields = self.Schema()['ResultsRange'].subfields @@ -160,17 +184,18 @@ def getResultsRangeDict(self): return specs security.declarePublic('getResultsRangeSorted') + def getResultsRangeSorted(self): - """ - Return an array of dictionaries, sorted by AS title: - [{'category': - 'service': <title of AS>, - 'id': <ID of AS> - 'uid': <UID of AS> - 'min': <min range spec value> - 'max': <max range spec value> - 'error': <error spec value> - ...}] + """Return an array of dictionaries, sorted by AS title: + + [{'category': <title of AS category> + 'service': <title of AS>, + 'id': <ID of AS> + 'uid': <UID of AS> + 'min': <min range spec value> + 'max': <max range spec value> + 'error': <error spec value> + ...}] """ tool = getToolByName(self, REFERENCE_CATALOG) @@ -180,37 +205,40 @@ def getResultsRangeSorted(self): service = tool.lookupObject(spec['uid']) service_title = service.Title() category_title = service.getCategoryTitle() - if not cats.has_key(category_title): + if category_title not in cats: cats[category_title] = {} cat = cats[category_title] - cat[service_title] = {'category': category_title, - 'service': service_title, - 'id': service.getId(), - 'uid': spec['uid'], - 'min': spec['min'], - 'max': spec['max'], - 'error': spec['error'] } + cat[service_title] = { + 'category': category_title, + 'service': service_title, + 'id': service.getId(), + 'uid': spec['uid'], + 'min': spec['min'], + 'max': spec['max'], + 'error': spec['error'] + } for key in subfields: if key not in ['uid', 'keyword']: cat[service_title][key] = spec.get(key, '') cat_keys = cats.keys() - cat_keys.sort(lambda x, y:cmp(x.lower(), y.lower())) + cat_keys.sort(lambda x, y: cmp(x.lower(), y.lower())) sorted_specs = [] for cat in cat_keys: services = cats[cat] service_keys = services.keys() - service_keys.sort(lambda x, y:cmp(x.lower(), y.lower())) + service_keys.sort(lambda x, y: cmp(x.lower(), y.lower())) for service_key in service_keys: sorted_specs.append(services[service_key]) return sorted_specs security.declarePublic('getRemainingSampleTypes') + def getSampleTypes(self): - """ return all sampletypes """ + """Return all sampletypes + """ sampletypes = [] bsc = getToolByName(self, 'bika_setup_catalog') - for st in bsc(portal_type = 'SampleType', - sort_on = 'sortable_title'): + for st in bsc(portal_type='SampleType', sort_on='sortable_title'): sampletypes.append((st.UID, st.Title)) return DisplayList(sampletypes) @@ -218,4 +246,5 @@ def getSampleTypes(self): def getClientUID(self): return self.aq_parent.UID() + atapi.registerType(AnalysisSpec, PROJECTNAME) From 5ad7b73ebc844d9f323f0769ce879343e060ed09 Mon Sep 17 00:00:00 2001 From: Ramon Bartl <rb@ridingbytes.com> Date: Thu, 21 Dec 2017 17:25:14 +0100 Subject: [PATCH 2/5] removed unused import --- bika/lims/browser/analysisrequest/add2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bika/lims/browser/analysisrequest/add2.py b/bika/lims/browser/analysisrequest/add2.py index f29caeda86..b931c8b73a 100644 --- a/bika/lims/browser/analysisrequest/add2.py +++ b/bika/lims/browser/analysisrequest/add2.py @@ -13,7 +13,6 @@ from BTrees.OOBTree import OOBTree from plone import protect -from plone.memoize import view from plone.memoize.volatile import cache from plone.memoize.volatile import DontCache From 5f1d52f1472c4ff7a64f91a6feea5b2dd2e5104c Mon Sep 17 00:00:00 2001 From: Ramon Bartl <rb@ridingbytes.com> Date: Thu, 21 Dec 2017 17:39:03 +0100 Subject: [PATCH 3/5] Fixed filter query for Analysisspec --- bika/lims/browser/analysisrequest/add2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bika/lims/browser/analysisrequest/add2.py b/bika/lims/browser/analysisrequest/add2.py index b931c8b73a..ea507511b7 100644 --- a/bika/lims/browser/analysisrequest/add2.py +++ b/bika/lims/browser/analysisrequest/add2.py @@ -1122,7 +1122,7 @@ def get_sampletype_info(self, obj): "sort_order": "descending", }, "specification": { - "getSampleTypeTitles": [obj.Title(), ''], + "getSampleTypeTitle": obj.Title(), "getClientUID": [client_uid, bika_analysisspecs_uid], "sort_order": "descending", } From 13d4d0cb8ff28368881dbfb51a0f68f841ad86a5 Mon Sep 17 00:00:00 2001 From: Ramon Bartl <rb@ridingbytes.com> Date: Thu, 21 Dec 2017 17:40:20 +0100 Subject: [PATCH 4/5] Changelog updated --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 655c60ab78..985299d0c0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,6 +21,7 @@ Changelog **Fixed** +- #489 Fix AR Add Form: No specifications found if a sample type was set - #475 Assigning Analyses to a WS raises AttributeError - #466 UnicodeDecodeError if unicode characters are entered into the title field - #453 Sample points do not show the referenced sample types in view From d4bf2ac964e0eeac8e41a4374490218b5f544969 Mon Sep 17 00:00:00 2001 From: Ramon Bartl <rb@ridingbytes.com> Date: Thu, 21 Dec 2017 17:42:30 +0100 Subject: [PATCH 5/5] Wron PR number in Changelog --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 985299d0c0..37554d4fdc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,7 +21,7 @@ Changelog **Fixed** -- #489 Fix AR Add Form: No specifications found if a sample type was set +- #490 Fix AR Add Form: No specifications found if a sample type was set - #475 Assigning Analyses to a WS raises AttributeError - #466 UnicodeDecodeError if unicode characters are entered into the title field - #453 Sample points do not show the referenced sample types in view