diff --git a/CHANGES.rst b/CHANGES.rst index fcfd20daeb..00dc8af46e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,7 @@ Changelog **Added** +- #1481 Filter Templates field when Sample Type is selected in Sample Add form - #1483 Added Accredited symbol in Analyses listings - #1466 Support for "readonly" and "hidden" visibility modes in ReferenceWidget diff --git a/bika/lims/browser/analysisrequest/add2.py b/bika/lims/browser/analysisrequest/add2.py index 6583db79f9..5760dd976b 100644 --- a/bika/lims/browser/analysisrequest/add2.py +++ b/bika/lims/browser/analysisrequest/add2.py @@ -1013,16 +1013,24 @@ def get_sampletype_info(self, obj): }) # catalog queries for UI field filtering + sample_type_uid = api.get_uid(obj) filter_queries = { + # Display Sample Points that have this sample type assigned plus + # those that do not have a sample type assigned "SamplePoint": { - "getSampleTypeTitles": [obj.Title(), ''], + "sampletype_uids": [sample_type_uid, None], "getClientUID": [client_uid, ""], - "sort_order": "descending", }, + # Display Specifications that have this sample type assigned only "Specification": { - "getSampleTypeTitle": obj.Title(), + "sampletype_uids": sample_type_uid, + "getClientUID": [client_uid, ""], + }, + # Display AR Templates that have this sample type assigned plus + # those that do not have a sample type assigned + "Template": { + "sampletype_uids": [sample_type_uid, None], "getClientUID": [client_uid, ""], - "sort_order": "descending", } } info["filter_queries"] = filter_queries @@ -1152,7 +1160,8 @@ def ajax_get_flush_settings(self): ], "SampleType": [ "SamplePoint", - "Specification" + "Specification", + "Template", ], "PrimarySample": [ "Batch" diff --git a/bika/lims/catalog/indexers/bikasetup.py b/bika/lims/catalog/indexers/bikasetup.py new file mode 100644 index 0000000000..e9ee811f1c --- /dev/null +++ b/bika/lims/catalog/indexers/bikasetup.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# This file is part of SENAITE.CORE. +# +# SENAITE.CORE is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright 2018-2019 by it's authors. +# Some rights reserved, see README and LICENSE. + +from plone.indexer import indexer + +from bika.lims.interfaces import IBikaSetupCatalog +from bika.lims.interfaces import ISampleTypeAwareMixin + + +@indexer(ISampleTypeAwareMixin, IBikaSetupCatalog) +def sampletype_uids(instance): + """Returns the list of SampleType UIDs the instance is assigned to + + This is a KeywordIndex, so it will be indexed as a list, even if only one + SampleType can be assigned to the instance. Moreover, if the instance has no + SampleType assigned, it returns a tuple with a None value. This allows + searches for `MissingValue` entries too. + """ + return instance.getSampleTypeUID() or (None, ) diff --git a/bika/lims/catalog/indexers/configure.zcml b/bika/lims/catalog/indexers/configure.zcml index a32374eabe..55e6990556 100644 --- a/bika/lims/catalog/indexers/configure.zcml +++ b/bika/lims/catalog/indexers/configure.zcml @@ -24,6 +24,14 @@ + + + + + + + diff --git a/bika/lims/content/analysisrequest.py b/bika/lims/content/analysisrequest.py index 2982dff1e0..227d7ead22 100644 --- a/bika/lims/content/analysisrequest.py +++ b/bika/lims/content/analysisrequest.py @@ -690,7 +690,7 @@ 'width': '30', 'label': _('Title'), 'align': 'left'}, - {'columnName': 'SampleTypeTitle', + {'columnName': 'getSampleTypeTitle', 'width': '70', 'label': _('SampleType'), 'align': 'left'}, diff --git a/bika/lims/content/analysisspec.py b/bika/lims/content/analysisspec.py index 114d74ba7d..4a3fccc4f1 100644 --- a/bika/lims/content/analysisspec.py +++ b/bika/lims/content/analysisspec.py @@ -19,29 +19,27 @@ # Some rights reserved, see README and LICENSE. from AccessControl import ClassSecurityInfo -from bika.lims import api -from bika.lims import bikaMessageFactory as _ -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.content.clientawaremixin import ClientAwareMixin -from bika.lims.interfaces import IAnalysisSpec, IDeactivable +from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin +from Products.ATExtensions.field.records import RecordsField from Products.Archetypes import atapi from Products.Archetypes.public import BaseFolder -from Products.Archetypes.public import ComputedField -from Products.Archetypes.public import ComputedWidget from Products.Archetypes.public import ReferenceWidget from Products.Archetypes.public import 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 -from bika.lims.interfaces import IClient +from bika.lims import api +from bika.lims import bikaMessageFactory as _ +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.content.clientawaremixin import ClientAwareMixin +from bika.lims.content.sampletype import SampleTypeAwareMixin +from bika.lims.interfaces import IAnalysisSpec +from bika.lims.interfaces import IDeactivable schema = Schema(( @@ -55,21 +53,6 @@ ), ), - 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, - ), - ), )) + BikaSchema.copy() + Schema(( RecordsField( @@ -119,21 +102,14 @@ "in lists and results reports instead of the real result.") ), ), - - ComputedField( - 'ClientUID', - expression="context.aq_parent.UID()", - widget=ComputedWidget( - visible=False, - ), - ), )) schema['description'].widget.visible = True schema['title'].required = True -class AnalysisSpec(BaseFolder, HistoryAwareMixin, ClientAwareMixin): +class AnalysisSpec(BaseFolder, HistoryAwareMixin, ClientAwareMixin, + SampleTypeAwareMixin): """Analysis Specification """ implements(IAnalysisSpec, IDeactivable) @@ -155,9 +131,7 @@ def Title(self): if self.title: title = self.title else: - sampletype = self.getSampleType() - if sampletype: - title = sampletype.Title() + title = self.getSampleTypeTitle() or "" return safe_unicode(title).encode('utf-8') def contextual_title(self): diff --git a/bika/lims/content/artemplate.py b/bika/lims/content/artemplate.py index 4e61f9b88a..72f8b260ca 100644 --- a/bika/lims/content/artemplate.py +++ b/bika/lims/content/artemplate.py @@ -21,17 +21,7 @@ import sys from AccessControl import ClassSecurityInfo -from bika.lims import api -from bika.lims import bikaMessageFactory as _ -from bika.lims.browser.fields.remarksfield import RemarksField -from bika.lims.browser.widgets import ARTemplateAnalysesWidget -from bika.lims.browser.widgets import ARTemplatePartitionsWidget -from bika.lims.browser.widgets import ReferenceWidget -from bika.lims.browser.widgets import RemarksWidget -from bika.lims.config import PROJECTNAME -from bika.lims.content.bikaschema import BikaSchema -from bika.lims.content.clientawaremixin import ClientAwareMixin -from bika.lims.interfaces import IARTemplate, IDeactivable +from Products.ATExtensions.field.records import RecordsField from Products.Archetypes.public import BaseContent from Products.Archetypes.public import BooleanField from Products.Archetypes.public import BooleanWidget @@ -42,10 +32,23 @@ from Products.Archetypes.public import Schema from Products.Archetypes.public import registerType from Products.Archetypes.references import HoldingReference -from Products.ATExtensions.field.records import RecordsField from Products.CMFCore.utils import getToolByName from zope.interface import implements +from bika.lims import api +from bika.lims import bikaMessageFactory as _ +from bika.lims.browser.fields.remarksfield import RemarksField +from bika.lims.browser.widgets import ARTemplateAnalysesWidget +from bika.lims.browser.widgets import ARTemplatePartitionsWidget +from bika.lims.browser.widgets import ReferenceWidget +from bika.lims.browser.widgets import RemarksWidget +from bika.lims.config import PROJECTNAME +from bika.lims.content.bikaschema import BikaSchema +from bika.lims.content.clientawaremixin import ClientAwareMixin +from bika.lims.content.sampletype import SampleTypeAwareMixin +from bika.lims.interfaces import IARTemplate +from bika.lims.interfaces import IDeactivable + schema = BikaSchema.copy() + Schema(( ReferenceField( "SamplePoint", @@ -96,13 +99,6 @@ showOn=True, ), ), - ComputedField( - "SampleTypeUID", - expression="context.Schema()['SampleType'].get(context) and context.Schema()['SampleType'].get(context).UID() or ''", - widget=ComputedWidget( - visible=False, - ), - ), BooleanField( "Composite", default=False, @@ -300,7 +296,7 @@ schema["title"]._validationLayer() -class ARTemplate(BaseContent, ClientAwareMixin): +class ARTemplate(BaseContent, ClientAwareMixin, SampleTypeAwareMixin): security = ClassSecurityInfo() schema = schema displayContentsTab = False diff --git a/bika/lims/content/samplepoint.py b/bika/lims/content/samplepoint.py index c15c44536f..e2bdd30f81 100644 --- a/bika/lims/content/samplepoint.py +++ b/bika/lims/content/samplepoint.py @@ -25,7 +25,6 @@ from Products.Archetypes.public import BaseContent from Products.Archetypes.public import BooleanField from Products.Archetypes.public import BooleanWidget -from Products.Archetypes.public import DisplayList from Products.Archetypes.public import FileWidget from Products.Archetypes.public import ReferenceField from Products.Archetypes.public import Schema @@ -34,8 +33,10 @@ from Products.Archetypes.public import registerType from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import safe_unicode +from plone.app.blob.field import FileField as BlobFileField +from zope.interface import implements + from bika.lims import bikaMessageFactory as _ -from bika.lims import deprecated from bika.lims.browser.fields import CoordinateField from bika.lims.browser.fields import DurationField from bika.lims.browser.widgets import CoordinateWidget @@ -45,9 +46,8 @@ from bika.lims.config import PROJECTNAME from bika.lims.content.bikaschema import BikaSchema from bika.lims.content.clientawaremixin import ClientAwareMixin +from bika.lims.content.sampletype import SampleTypeAwareMixin from bika.lims.interfaces import IDeactivable -from plone.app.blob.field import FileField as BlobFileField -from zope.interface import implements schema = BikaSchema.copy() + Schema(( CoordinateField( @@ -91,13 +91,17 @@ required=0, multiValued=1, allowed_types=('SampleType',), - vocabulary='SampleTypesVocabulary', relationship='SamplePointSampleType', widget=BikaReferenceWidget( label=_("Sample Types"), description=_("The list of sample types that can be collected " "at this sample point. If no sample types are " "selected, then all sample types are available."), + catalog_name='bika_setup_catalog', + base_query={"is_active": True, + "sort_on": "sortable_title", + "sort_order": "ascending"}, + showOn=True, ), ), @@ -126,7 +130,8 @@ schema['description'].schemata = 'default' -class SamplePoint(BaseContent, HistoryAwareMixin, ClientAwareMixin): +class SamplePoint(BaseContent, HistoryAwareMixin, ClientAwareMixin, + SampleTypeAwareMixin): implements(IDeactivable) security = ClassSecurityInfo() displayContentsTab = False @@ -141,31 +146,6 @@ def _renameAfterCreation(self, check_auto_id=False): def Title(self): return safe_unicode(self.getField('title').get(self)).encode('utf-8') - def getSampleTypeTitles(self): - """Returns a list of sample type titles - """ - sample_types = self.getSampleTypes() - sample_type_titles = map(lambda obj: obj.Title(), sample_types) - - # N.B. This is used only for search purpose, because the catalog does - # not add an entry to the Keywordindex for an empty list. - # - # => This "empty" category allows to search for values with a certain - # sample type set OR with no sample type set. - # (see bika.lims.browser.analysisrequest.add2.get_sampletype_info) - if not sample_type_titles: - return [""] - return sample_type_titles - - def getSampleTypeTitle(self): - """Returns a comma separated list of sample type titles - """ - return ",".join(self.getSampleTypeTitles()) - - def SampleTypesVocabulary(self): - from bika.lims.content.sampletype import SampleTypes - return SampleTypes(self, allow_blank=False) - def setSampleTypes(self, value, **kw): """ For the moment, we're manually trimming the sampletype<>samplepoint relation to be equal on both sides, here. @@ -200,31 +180,5 @@ def setSampleTypes(self, value, **kw): return ret - def getSampleTypes(self, **kw): - return self.Schema()['SampleTypes'].get(self) - registerType(SamplePoint, PROJECTNAME) - - -@deprecated("bika.lims.content.samplepoint.SamplePoints function will be removed in senaite.core 1.2.0") -def SamplePoints(self, instance=None, allow_blank=True, lab_only=True): - instance = instance or self - bsc = getToolByName(instance, 'bika_setup_catalog') - items = [] - contentFilter = { - 'portal_type': 'SamplePoint', - 'is_active': True, - 'sort_on': 'sortable_title'} - if lab_only: - lab_path = instance.bika_setup.bika_samplepoints.getPhysicalPath() - contentFilter['path'] = {"query": "/".join(lab_path), "level": 0} - for sp in bsc(contentFilter): - sp = sp.getObject() - if sp.aq_parent.portal_type == 'Client': - sp_title = "{}: {}".format(sp.aq_parent.Title(), sp.Title()) - else: - sp_title = sp.Title() - items.append((sp.UID(), sp_title)) - items = allow_blank and [['', '']] + list(items) or list(items) - return DisplayList(items) diff --git a/bika/lims/content/sampletype.py b/bika/lims/content/sampletype.py index 01269926f0..532769a9e1 100644 --- a/bika/lims/content/sampletype.py +++ b/bika/lims/content/sampletype.py @@ -25,6 +25,10 @@ from Products.Archetypes.references import HoldingReference from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import safe_unicode +from magnitude import mg +from zope.interface import implements + +from bika.lims import api from bika.lims import bikaMessageFactory as _ from bika.lims import logger from bika.lims.browser.fields import DurationField @@ -33,10 +37,10 @@ from bika.lims.browser.widgets.referencewidget import ReferenceWidget as brw from bika.lims.config import PROJECTNAME from bika.lims.content.bikaschema import BikaSchema -from bika.lims.interfaces import ISampleType, IDeactivable +from bika.lims.interfaces import IDeactivable +from bika.lims.interfaces import ISampleType +from bika.lims.interfaces import ISampleTypeAwareMixin from bika.lims.vocabularies import getStickerTemplates -from magnitude import mg -from zope.interface import implements SMALL_DEFAULT_STICKER = 'small_default' LARGE_DEFAULT_STICKER = 'large_default' @@ -56,6 +60,58 @@ def sticker_templates(): return voc +class SampleTypeAwareMixin(BaseObject): + implements(ISampleTypeAwareMixin) + + security = ClassSecurityInfo() + + @security.public + def getSampleType(self): + """Returns the sample type(s) assigned to this object, if any + """ + if ISampleType.providedBy(self): + return self + + field = self._get_field() + if not field: + return None + + sample_type = field.get(self) + return sample_type or None + + @security.public + def getSampleTypeUID(self): + """Returns the UID(s) of the Sample Type(s) assigned to this object + """ + sample_type = self.getSampleType() + if isinstance(sample_type, (list, tuple)): + return map(api.get_uid, sample_type) + elif sample_type: + return api.get_uid(sample_type) + return None + + @security.public + def getSampleTypeTitle(self): + """Returns the title or a comma separated list of sample type titles + """ + sample_type = self.getSampleType() + if isinstance(sample_type, (list, tuple)): + title = map(api.get_title, sample_type) + return ", ".join(title) + elif sample_type: + return api.get_title(sample_type) + return None + + def _get_field(self): + """Returns the field that stores the SampleType object, if any + """ + field = self.getField("SampleType", None) + if not field: + field = self.getField("SampleTypes", None) + + return field + + schema = BikaSchema.copy() + Schema(( DurationField('RetentionPeriod', required = 1, @@ -119,13 +175,17 @@ def sticker_templates(): required = 0, multiValued = 1, allowed_types = ('SamplePoint',), - vocabulary = 'SamplePointsVocabulary', relationship = 'SampleTypeSamplePoint', widget = brw( label=_("Sample Points"), description =_("The list of sample points from which this sample " "type can be collected. If no sample points are " "selected, then all sample points are available."), + catalog_name='bika_setup_catalog', + base_query={"is_active": True, + "sort_on": "sortable_title", + "sort_order": "ascending"}, + showOn=True, ), ), ComputedField( @@ -181,7 +241,8 @@ def sticker_templates(): schema['description'].schemata = 'default' schema['description'].widget.visible = True -class SampleType(BaseContent, HistoryAwareMixin): + +class SampleType(BaseContent, HistoryAwareMixin, SampleTypeAwareMixin): implements(ISampleType, IDeactivable) security = ClassSecurityInfo() @@ -221,10 +282,6 @@ def getDefaultLifetime(self): settings = getToolByName(self, 'bika_setup') return settings.getDefaultSampleLifetime() - def SamplePointsVocabulary(self): - from bika.lims.content.samplepoint import SamplePoints - return SamplePoints(self, allow_blank=False, lab_only=False) - def setSamplePoints(self, value, **kw): """ For the moment, we're manually trimming the sampletype<>samplepoint relation to be equal on both sides, here. @@ -257,9 +314,6 @@ def setSamplePoints(self, value, **kw): return ret - def getSamplePoints(self, **kw): - return self.Schema()['SamplePoints'].get(self) - def SampleMatricesVocabulary(self): from bika.lims.content.samplematrix import SampleMatrices return SampleMatrices(self, allow_blank=True) @@ -367,14 +421,3 @@ def _set_sticker_subfield(self, subfield, value): registerType(SampleType, PROJECTNAME) - -def SampleTypes(self, instance=None, allow_blank=False): - instance = instance or self - bsc = getToolByName(instance, 'bika_setup_catalog') - items = [] - for st in bsc(portal_type='SampleType', - is_active=True, - sort_on = 'sortable_title'): - items.append((st.UID, st.Title)) - items = allow_blank and [['','']] + list(items) or list(items) - return DisplayList(items) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 3116794db7..b74fc273d7 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -999,3 +999,21 @@ def getClient(self): def getClientUID(self): """Returns the client UID this object is bound to, if any """ + + +class ISampleTypeAwareMixin(Interface): + """Marker interface for objects that can be assigned to one, or multiple + SampleType objects through a ReferenceField + """ + + def getSampleType(self): + """Returns the sample type(s) assigned to this object, if any + """ + + def getSampleTypeUID(self): + """Returns the UID(s) of the Sample Type(s) assigned to this object + """ + + def getSampleTypeTitle(self): + """Returns the title or a comma separated list of sample type titles + """ diff --git a/bika/lims/setuphandlers.py b/bika/lims/setuphandlers.py index f735ee1a27..489dae7fc2 100644 --- a/bika/lims/setuphandlers.py +++ b/bika/lims/setuphandlers.py @@ -149,8 +149,6 @@ ("bika_catalog", "getExpiryDate", "", "DateIndex"), ("bika_catalog", "getId", "", "FieldIndex"), ("bika_catalog", "getReferenceDefinitionUID", "", "FieldIndex"), - ("bika_catalog", "getSampleTypeTitle", "", "FieldIndex"), - ("bika_catalog", "getSampleTypeUID", "", "FieldIndex"), ("bika_catalog", "getSupportedServices", "", "KeywordIndex"), ("bika_catalog", "id", "getId", "FieldIndex"), ("bika_catalog", "isValid", "", "BooleanIndex"), @@ -202,9 +200,10 @@ ("bika_setup_catalog", "getPrice", "", "FieldIndex"), ("bika_setup_catalog", "getSamplePointTitle", "", "KeywordIndex"), ("bika_setup_catalog", "getSamplePointUID", "", "FieldIndex"), + # Sorting of listings: Sample Points, Specifications ("bika_setup_catalog", "getSampleTypeTitle", "", "FieldIndex"), - ("bika_setup_catalog", "getSampleTypeTitles", "", "KeywordIndex"), - ("bika_setup_catalog", "getSampleTypeUID", "", "FieldIndex"), + # Filter in Add2: Sample Points, Specifications, Templates + ("bika_setup_catalog", "sampletype_uids", "", "KeywordIndex"), ("bika_setup_catalog", "getServiceUID", "", "FieldIndex"), ("bika_setup_catalog", "getServiceUIDs", "", "KeywordIndex"), ("bika_setup_catalog", "getTotalPrice", "", "FieldIndex"), @@ -238,7 +237,6 @@ ("bika_catalog", "getClientTitle"), ("bika_catalog", "getClientID"), ("bika_catalog", "getClientBatchID"), - ("bika_catalog", "getSampleTypeTitle"), ("bika_catalog", "getDateReceived"), ("bika_catalog", "getDateSampled"), ("bika_catalog", "review_state"), @@ -283,8 +281,6 @@ ("bika_setup_catalog", "getPrice"), ("bika_setup_catalog", "getSamplePointTitle"), ("bika_setup_catalog", "getSamplePointUID"), - ("bika_setup_catalog", "getSampleTypeTitle"), - ("bika_setup_catalog", "getSampleTypeUID"), ("bika_setup_catalog", "getServiceUID"), ("bika_setup_catalog", "getTotalPrice"), ("bika_setup_catalog", "getUnit"), diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 5775233b8e..07a0ec8d6b 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -30,6 +30,49 @@ version = "1.3.3" # Remember version number in metadata.xml and setup.py profile = "profile-{0}:default".format(product) +INDEXES_TO_ADD = [ + # We changed the type of this index from FieldIndex to KeywordIndex + # https://github.com/senaite/senaite.core/pull/1481 + ("bika_setup_catalog", "sampletype_uids", "KeywordIndex"), +] + +INDEXES_TO_REMOVE = [ + # Only used in add2 to filter Sample Points by Sample Type when a Sample + # Type was selected. Now, getSampleTypeUID is used instead because of + # https://github.com/senaite/senaite.core/pull/1481 + ("bika_setup_catalog", "getSampleTypeTitles"), + + # Only used for when Sample and SamplePartition objects + # existed. The following are the portal types stored in bika_catalog: + # Batch, BatchFolder and ReferenceSample + # and there are no searches by getSampleTypeTitle for none of them + ("bika_catalog", "getSampleTypeTitle"), + + # Not used neither for searches nor filtering of any of the content types + # stored in bika_catalog (Batch, BatchFolder and ReferenceSample) + ("bika_catalog", "getSampleTypeUID"), + + # We remove this index because we changed it's type to KeywordIndex + # https://github.com/senaite/senaite.core/pull/1481 + ("bika_setup_catalog", "getSampleTypeUID") +] + +METADATA_TO_REMOVE = [ + # Not used anywhere. In SamplePoints and Specifications listings, the + # SampleType object is waken-up instead of calling the metadata + ("bika_setup_catalog", "getSampleTypeTitle"), + + # getSampleTypeUID (as metadata field) is only used for analyses and + # samples (AnalysisRequest), and none of the two are stored in setup_catalog + ("bika_setup_catalog", "getSampleTypeUID"), + + # Only used for when Sample and SamplePartition objects existed. + # The following are the portal types stored in bika_catalog: + # Batch, BatchFolder and ReferenceSample + # and "getSampleTypeTitle" metadata is not used for none of them + ("bika_catalog", "getSampleTypeTitle") +] + @upgradestep(product, version) def upgrade(tool): @@ -50,6 +93,15 @@ def upgrade(tool): # https://github.com/senaite/senaite.core/pull/1469 setup.runImportStepFromProfile(profile, "propertiestool") + # Remove stale indexes and metadata + # https://github.com/senaite/senaite.core/pull/1481 + remove_stale_indexes(portal) + remove_stale_metadata(portal) + + # Add new indexes + # https://github.com/senaite/senaite.core/pull/1481 + add_new_indexes(portal) + # Reindex client's related fields (getClientUID, getClientTitle, etc.) # https://github.com/senaite/senaite.core/pull/1477 reindex_client_fields(portal) @@ -92,3 +144,58 @@ def reindex_client_fields(portal): obj.reindexObject(idxs=fields_to_reindex) logger.info("Reindexing client fields ... [DONE]") + + +def remove_stale_indexes(portal): + logger.info("Removing stale indexes ...") + for catalog, index in INDEXES_TO_REMOVE: + del_index(catalog, index) + logger.info("Removing stale indexes ... [DONE]") + + +def remove_stale_metadata(portal): + logger.info("Removing stale metadata ...") + for catalog, column in METADATA_TO_REMOVE: + del_metadata(catalog, column) + logger.info("Removing stale metadata ... [DONE]") + + +def del_index(catalog_id, index_name): + logger.info("Removing '{}' index from '{}' ..." + .format(index_name, catalog_id)) + catalog = api.get_tool(catalog_id) + if index_name not in catalog.indexes(): + logger.info("Index '{}' not in catalog '{}' [SKIP]" + .format(index_name, catalog_id)) + return + catalog.delIndex(index_name) + + +def del_metadata(catalog_id, column): + logger.info("Removing '{}' metadata from '{}' ..." + .format(column, catalog_id)) + catalog = api.get_tool(catalog_id) + if column not in catalog.schema(): + logger.info("Metadata '{}' not in catalog '{}' [SKIP]" + .format(column, catalog_id)) + return + catalog.delColumn(column) + + +def add_new_indexes(portal): + logger.info("Adding new indexes ...") + for catalog_id, index_name, index_metatype in INDEXES_TO_ADD: + add_index(catalog_id, index_name, index_metatype) + logger.info("Adding new indexes ... [DONE]") + + +def add_index(catalog_id, index_name, index_metatype): + logger.info("Adding '{}' index to '{}' ...".format(index_name, catalog_id)) + catalog = api.get_tool(catalog_id) + if index_name in catalog.indexes(): + logger.info("Index '{}' already in catalog '{}' [SKIP]" + .format(index_name, catalog_id)) + return + catalog.addIndex(index_name, index_metatype) + logger.info("Indexing new index '{}' ...".format(index_name)) + catalog.manage_reindexIndex(index_name) diff --git a/bika/lims/vocabularies/__init__.py b/bika/lims/vocabularies/__init__.py index 8346228e23..3ffcc0eeac 100644 --- a/bika/lims/vocabularies/__init__.py +++ b/bika/lims/vocabularies/__init__.py @@ -214,16 +214,6 @@ def __init__(self): SamplePointVocabularyFactory = SamplePointVocabulary() -class SampleTypeVocabulary(BikaContentVocabulary): - def __init__(self): - BikaContentVocabulary.__init__(self, - ['bika_setup/bika_sampletypes', ], - ['SampleType', ]) - - -SampleTypeVocabularyFactory = SampleTypeVocabulary() - - class AnalysisServiceVocabulary(BikaContentVocabulary): def __init__(self): BikaContentVocabulary.__init__(self, diff --git a/bika/lims/vocabularies/configure.zcml b/bika/lims/vocabularies/configure.zcml index 0c7b10385a..57ccb9bf27 100644 --- a/bika/lims/vocabularies/configure.zcml +++ b/bika/lims/vocabularies/configure.zcml @@ -50,11 +50,6 @@ name="bika.lims.vocabularies.StorageLocations" /> - -