Completed
Push — master ( e1228c...abc836 )
by Jordi
04:46
created

BaseCatalog.clearFindAndRebuild()   B

Complexity

Conditions 5

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 49
rs 8.7173
c 0
b 0
f 0
cc 5
nop 1
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2019 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import transaction
22
from AccessControl import ClassSecurityInfo
23
from AccessControl.Permissions import \
24
    manage_zcatalog_entries as ManageZCatalogEntries
25
from Acquisition import aq_inner
26
from Acquisition import aq_parent
27
from App.class_init import InitializeClass
28
from bika.lims import api
29
from bika.lims import logger
30
from plone.dexterity.interfaces import IDexterityFTI
31
from Products.CMFPlone.CatalogTool import CatalogTool
32
from Products.CMFPlone.utils import base_hasattr
33
from Products.CMFPlone.utils import safe_callable
34
from Products.ZCatalog.ZCatalog import ZCatalog
35
36
37
class BaseCatalog(CatalogTool):
38
    """Base class for specialized catalogs
39
    """
40
41
    security = ClassSecurityInfo()
42
43
    def __init__(self, id, title, portal_meta_type):
44
        self.portal_type = portal_meta_type
45
        self.meta_type = portal_meta_type
46
        self.title = title
47
        self.counter = 0
48
        ZCatalog.__init__(self, id)
49
50
    def get_mapped_types(self):
51
        """Returns the mapped types for this catalog
52
        """
53
        pt = api.get_tool("portal_types")
54
        at = api.get_tool("archetype_tool")
55
56
        # Get all Archetypes which are mapped to this catalog
57
        at_types = [k for k, v in at.catalog_map.items() if self.id in v]
58
59
        # TODO: Discover the mapped catalogs for Dexterity types
60
        dx_ftis = filter(lambda fti: IDexterityFTI.providedBy(fti),
61
                         pt.listTypeInfo())
62
        dx_types = map(lambda fti: fti.getId(), dx_ftis)
63
64
        return at_types + dx_types
65
66
    def get_portal_type(self, obj):
67
        """Returns the portal type of the object
68
        """
69
        if not api.is_object(obj):
70
            return None
71
        return api.get_portal_type(obj)
72
73
    def is_indexable(self, obj):
74
        """Checks if the object can be indexed
75
        """
76
        if not (base_hasattr(obj, "reindexObject")):
77
            return False
78
        if not (safe_callable(obj.reindexObject)):
79
            return False
80
        return True
81
82
    @security.protected(ManageZCatalogEntries)
83
    def clearFindAndRebuild(self):
84
        # Empties catalog, then finds all contentish objects (i.e. objects
85
        # with an indexObject method), and reindexes them.
86
        # This may take a long time.
87
88
        # The Catalog ID
89
        cid = self.getId()
90
91
        # The catalog indexes
92
        idxs = list(self.indexes())
93
94
        # Types to consider for this catalog
95
        obj_metatypes = self.get_mapped_types()
96
97
        def indexObject(obj, path):
98
            __traceback_info__ = path
99
100
            # skip non-indexable types
101
            if not self.is_indexable(obj):
102
                return
103
104
            # skip types that are not mapped to this catalog
105
            if self.get_portal_type(obj) not in obj_metatypes:
106
                return
107
108
            self.counter += 1
109
110
            try:
111
                obj.reindexObject(idxs=idxs)
112
            except TypeError:
113
                # Catalogs have 'indexObject' as well, but they
114
                # take different args, and will fail
115
                pass
116
117
            if self.counter % 100 == 0:
118
                logger.info("Progress: {} objects have been cataloged for {}."
119
                            .format(self.counter, cid))
120
                transaction.savepoint(optimistic=True)
121
122
        logger.info("Cleaning and rebuilding catalog '{}'...".format(cid))
123
        self.counter = 0
124
        self.manage_catalogClear()
125
        portal = aq_parent(aq_inner(self))
126
        portal.ZopeFindAndApply(
127
            portal,
128
            search_sub=True,
129
            apply_func=indexObject)
130
        logger.info("Catalog '{}' cleaned and rebuilt".format(cid))
131
132
133
InitializeClass(BaseCatalog)
134