diff --git a/README.txt b/README.txt index 6e1d6d5..6f86ec7 100755 --- a/README.txt +++ b/README.txt @@ -940,6 +940,12 @@ Security >>> from loops.security.browser import admin, audit +Paster Shell Utilities - Repair Scripts +======================================= + + >>> from loops.repair.base import removeRecords + + Import/Export ============= diff --git a/psu.py b/psu.py new file mode 100644 index 0000000..7626229 --- /dev/null +++ b/psu.py @@ -0,0 +1,159 @@ +# psu - paster shell utilities +# use this from (e.g.): +# +# bin/paster shell deploy.ini +# +# then: +# +# from loops import psu +# psu.setup(root) +# obj = psu.byuid('578457950') +# + +from transaction import commit, abort +from zope.app.authentication.principalfolder import Principal +from zope.app.component.hooks import setSite +from zope.app.container.contained import ObjectAddedEvent, ObjectRemovedEvent +from zope.cachedescriptors.property import Lazy +from zope.catalog.interfaces import ICatalog +from zope import component +from zope.event import notify +from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent +from zope.publisher.browser import TestRequest as BaseTestRequest +from zope.security.management import getInteraction, newInteraction, endInteraction +from zope.interface import Interface + +from cybertools.util.jeep import Jeep +from loops.common import adapted, baseObject +from loops.util import getObjectForUid, getUidForObject +#from xxx import config + + +sc = Jeep() # shortcuts + + +def setup(root): + global sm, smdefault, intids, pau, sc + setSite(root) + sm = component.getSiteManager(root) + smdefault = sm['default'] + intids = smdefault['IntIds'] + pau = smdefault['PluggableAuthentication'] + #user = getattr(config, 'shell_user', 'zope.manager') + #password = (getattr(config, 'shell_pw', None) or + # raw_input('Enter manager password: ')) + user = 'zope.manager' + passwort = raw_input('Enter manager password: ') + login(Principal(user, password, u'Manager')) + + +def byuid(uid): + return getObjectForUid(uid) + +def uid(obj): + return getUidForObject(obj) + +def notifyModification(obj): + obj = baseObject(obj) + notify(ObjectModifiedEvent(obj)) + +def save(obj): + notifyModification(obj) + commit() + +def notifyAdded(obj): + obj = baseObject(obj) + notify(ObjectAddedEvent(obj)) + +def notifyRemoved(obj): + obj = baseObject(obj) + notify(ObjectRemovedEvent(obj)) + +def delete(container, name): + obj = container.get(name) + if obj is None: + print '*** Object', name, 'not found!' + return + notifyRemoved(obj) + del container[name] + commit() + +def rename(container, old, new): + obj = container.get(old) + if obj is None: + print '*** Object', old, 'not found!' + return + container[new] = obj + #notifyAdded(obj) + notifyModification(obj) + commit() + +def move(source, target, name): + obj = source.get(name) + if obj is None: + print '*** Object', name, 'not found!' + return + #notifyRemoved(obj) + #del source[name] + target[name] = obj + #notifyAdded(obj) + notifyModification(obj) + commit() + +def get(container, obj): + if isinstance(obj, basestring): + name = obj + obj = container.get(name) + if obj is None: + print '*** Object', name, 'not found!' + return None + return adapted(obj) + +# catalog / indexing + +def getCatalog(context): + context = baseObject(context) + for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=context): + return cat + print '*** No catalog found!' + +def reindex(obj, catalog=None): + obj = baseObject(obj) + if catalog is None: + catalog = getCatalog(obj) + if catalog is not None: + catalog.index_doc(int(getUidForObject(obj)), obj) + + +# helper functions and classes + +def login(principal): + endInteraction() + newInteraction(Participation(principal)) + + +class TestRequest(BaseTestRequest): + + basePrincipal = BaseTestRequest.principal + + @Lazy + def principal(self): + interaction = getInteraction() + if interaction is not None: + parts = interaction.participations + if parts: + prin = parts[0].principal + if prin is not None: + return prin + return self.basePrincipal + + +class Participation(object): + """ Dummy Participation class for testing. + """ + + interaction = None + + def __init__(self, principal): + self.principal = principal + diff --git a/repair/__init__.py b/repair/__init__.py new file mode 100644 index 0000000..401df7c --- /dev/null +++ b/repair/__init__.py @@ -0,0 +1 @@ +# package loops.repair \ No newline at end of file diff --git a/repair/base.py b/repair/base.py new file mode 100644 index 0000000..bd0f5a0 --- /dev/null +++ b/repair/base.py @@ -0,0 +1,117 @@ +# +# Copyright (c) 2016 Helmut Merz helmutm@cy55.de +# +# This program 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Basic stuff for database fixes. +""" + +import os + +from cybertools.util.date import date2TimeStamp, strptime +from cybertools.util.jeep import Jeep +from loops.common import baseObject +from loops import psu + +os.environ['NLS_LANG'] = 'German_Germany.UTF8' + + +# start, loop, finish... + +def startup(msg, **kw): + print '***', msg + step = kw.pop('step', 10) + return Jeep(count=0, step=step, message=msg, **kw) + +def update(fct, obj, info): + info.count += 1 + start = info.get('start') + if start and info.count < start: + return + if info.count % info.step == 0: + try: + objInfo = obj.__name__ + except: + try: + objInfo = obj.context.__name__ + except: + objInfo = obj + print '*** Processing object # %i: %s.' % (info.count, objInfo) + if info.get('updated'): + print '*** updated: %i.' % info.updated + psu.commit() + return fct(obj, info) + +def finish(info): + print '*** count: %i.' % info.count + if info.get('updated'): + print '*** updated: %i.' % info.updated + psu.commit() + +def stop_condition(info): + stop = info.get('stop') + return stop is not None and info.count > stop + + +# generic loop + +def loop(message, objects, fct, **kw): + def _fct(obj, info): + params = info.get('fctparams', {}) + fct(obj, **params) + info = startup(message, **kw) + for obj in objects: + update(_fct, obj, info) + if stop_condition(info): + break + finish(info) + + +# auxiliary functions + +def get_type_instances(name): + return psu.sc.concepts[name].getChildren([psu.sc.hasType]) + +def notify_modification(c, info): + psu.notifyModification(c) + + +# some common repair tasks + +def removeRecords(container, **kw): + """Remove records from container selected by the criteria given.""" + + def remove(obj, info): + psu.notifyRemoved(obj) + del info.container[obj.__name__] + + info = startup('Remove records', container=container, **kw) + dateTo = kw.pop('dateTo', None) + if dateTo: + timeTo = date2TimeStamp(strptime(dateTo + ' 23:59:59')) + kw['timeTo'] = timeTo + date = kw.pop('date', None) + if date: + kw['timeFromTo'] = ( + date2TimeStamp(strptime(date + ' 00:00:00')), + date2TimeStamp(strptime(date + ' 23:59:59'))) + for obj in container.query(**kw): + update(remove, obj, info) + if stop_condition(info): + break + finish(info) +