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"
/>
-
-