[3142] | 1 | import logging |
---|
| 2 | import types |
---|
[1764] | 3 | import transaction |
---|
[1754] | 4 | from zope.interface import implements |
---|
[1865] | 5 | from zope.component import getMultiAdapter |
---|
[1754] | 6 | from zope.component import queryMultiAdapter |
---|
| 7 | |
---|
| 8 | from Missing import MV |
---|
| 9 | from Acquisition import aq_parent |
---|
| 10 | |
---|
| 11 | from Products.CMFCore.utils import getToolByName |
---|
| 12 | from Products.ZCatalog.Catalog import safe_callable |
---|
| 13 | |
---|
[1865] | 14 | try: |
---|
| 15 | from plone.indexer.interfaces import IIndexableObject |
---|
| 16 | except ImportError: |
---|
| 17 | from plone.app.content.interfaces import IIndexableObjectWrapper \ |
---|
| 18 | as _old_IIndexableObjectWrapper |
---|
| 19 | IS_NEW = False |
---|
[3142] | 20 | else: |
---|
[1865] | 21 | IS_NEW = True |
---|
| 22 | |
---|
| 23 | |
---|
[1754] | 24 | from quintagroup.catalogupdater.interfaces import ICatalogUpdater |
---|
| 25 | |
---|
| 26 | LOG = logging.getLogger('quintagroup.catalogupdater') |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | class CatalogUpdaterUtility(object): |
---|
| 30 | |
---|
| 31 | implements(ICatalogUpdater) |
---|
| 32 | |
---|
[1755] | 33 | def validate(self, cat, cols): |
---|
[1754] | 34 | # Validate catalog and column name |
---|
[3142] | 35 | AVAIL_COLTYPES = list(types.StringTypes) + [types.ListType, |
---|
| 36 | types.TupleType] |
---|
[1755] | 37 | |
---|
[1754] | 38 | _cat = getattr(cat, '_catalog', None) |
---|
| 39 | if _cat is None: |
---|
| 40 | raise AttributeError("%s - is not ZCatalog based catalog" % cat) |
---|
| 41 | |
---|
[1755] | 42 | if not type(cols) in AVAIL_COLTYPES: |
---|
[3142] | 43 | raise TypeError("'columns' parameter must be one of the " \ |
---|
| 44 | "following types: %s" % AVAIL_COLTYPES) |
---|
[1755] | 45 | # Normalize columns |
---|
| 46 | if type(cols) in types.StringTypes: |
---|
[3142] | 47 | cols = [cols, ] |
---|
[1755] | 48 | # Check is every column present in the catalog |
---|
| 49 | for col in cols: |
---|
[3142] | 50 | if not col in _cat.schema: |
---|
| 51 | raise AttributeError("'%s' - not presented column in " \ |
---|
| 52 | "%s catalog " % (col, cat)) |
---|
[1754] | 53 | |
---|
[1755] | 54 | return _cat, cols |
---|
| 55 | |
---|
[1865] | 56 | def getWrappedObjectNew(self, obj, portal, catalog): |
---|
| 57 | # Returned wrapped 'obj' object with IIndexable wrapper |
---|
[1981] | 58 | wrapper = None |
---|
[1865] | 59 | if not IIndexableObject.providedBy(obj): |
---|
[3142] | 60 | # This is the CMF 2.2 compatible approach, |
---|
| 61 | # which should be used going forward |
---|
| 62 | wrapper = queryMultiAdapter((obj, catalog), IIndexableObject) |
---|
[1981] | 63 | return wrapper and wrapper or obj |
---|
[1754] | 64 | |
---|
[1865] | 65 | def getWrappedObjectOld(self, obj, portal, catalog): |
---|
| 66 | # Returned wrapped 'obj' object with IIndexable wrapper |
---|
| 67 | wf = getattr(self, 'portal_workflow', None) |
---|
| 68 | # A comment for all the frustrated developers which aren't able to pin |
---|
| 69 | # point the code which adds the review_state to the catalog. :) |
---|
| 70 | # The review_state var and some other workflow vars are added to the |
---|
| 71 | # indexable object wrapper throught the code in the following lines |
---|
| 72 | if wf is not None: |
---|
| 73 | vars = wf.getCatalogVariablesFor(obj) |
---|
| 74 | else: |
---|
| 75 | vars = {} |
---|
[3142] | 76 | |
---|
[1865] | 77 | w = getMultiAdapter((obj, portal), _old_IIndexableObjectWrapper) |
---|
| 78 | w.update(vars) |
---|
| 79 | |
---|
| 80 | return w |
---|
| 81 | |
---|
[1755] | 82 | def updateMetadata4All(self, catalog, columns): |
---|
[1754] | 83 | """ Look into appropriate method of ICatalogUpdate interface |
---|
| 84 | """ |
---|
| 85 | |
---|
[1755] | 86 | _catalog, columns = self.validate(catalog, columns) |
---|
| 87 | |
---|
[1754] | 88 | portal = getToolByName(catalog, 'portal_url').getPortalObject() |
---|
| 89 | root = aq_parent(portal) |
---|
[3142] | 90 | |
---|
[1754] | 91 | data = _catalog.data |
---|
| 92 | schema = _catalog.schema |
---|
| 93 | paths = _catalog.paths |
---|
[3142] | 94 | getWrappedObject = (IS_NEW and self.getWrappedObjectNew |
---|
| 95 | or self.getWrappedObjectOld) |
---|
[1764] | 96 | # For subtransaction support |
---|
| 97 | threshold = getattr(catalog, 'threshold', 10000) |
---|
| 98 | _v_total = 0 |
---|
| 99 | _v_transaction = None |
---|
[1754] | 100 | |
---|
| 101 | # For each catalog record update metadata |
---|
| 102 | for rid, md in data.items(): |
---|
| 103 | # get an object |
---|
| 104 | obj_uid = paths[rid] |
---|
| 105 | try: |
---|
| 106 | obj = root.unrestrictedTraverse(obj_uid) |
---|
[1865] | 107 | obj = getWrappedObject(obj, portal, catalog) |
---|
[1754] | 108 | except: |
---|
[1755] | 109 | LOG.error('updateMetadata4All could not resolve ' |
---|
[1754] | 110 | 'an object from the uid %r.' % obj_uid) |
---|
| 111 | continue |
---|
| 112 | |
---|
| 113 | mdlist = list(md) |
---|
[1755] | 114 | for column in columns: |
---|
| 115 | # calculate the column value |
---|
[3142] | 116 | attr = getattr(obj, column, MV) |
---|
| 117 | if (attr is not MV and safe_callable(attr)): |
---|
| 118 | attr = attr() |
---|
[1755] | 119 | # Update metadata value |
---|
| 120 | indx = schema[column] |
---|
| 121 | mdlist[indx] = attr |
---|
[1754] | 122 | |
---|
[1755] | 123 | # Update catalog record |
---|
[1754] | 124 | data[rid] = tuple(mdlist) |
---|
| 125 | |
---|
[1764] | 126 | # Steeled from ZCatalog |
---|
| 127 | if threshold is not None: |
---|
| 128 | # figure out whether or not to commit a subtransaction. |
---|
| 129 | t = id(transaction.get()) |
---|
| 130 | if t != _v_transaction: |
---|
| 131 | _v_total = 0 |
---|
| 132 | _v_transaction = t |
---|
| 133 | _v_total = _v_total + 1 |
---|
| 134 | if _v_total > threshold: |
---|
| 135 | transaction.savepoint(optimistic=True) |
---|
| 136 | catalog._p_jar.cacheGC() |
---|
| 137 | _v_total = 0 |
---|
| 138 | LOG.info('commiting subtransaction') |
---|