move psu from loops.common to loops.server; provide server.main for easy starting as server
This commit is contained in:
parent
2a689a871b
commit
dc87e32a63
6 changed files with 319 additions and 3 deletions
|
@ -5,8 +5,12 @@ from os import getenv
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
zope_conf = getenv('ZOPE_CONF', 'zope.conf')
|
||||||
server_port = getenv('SERVER_PORT', '8099')
|
server_port = getenv('SERVER_PORT', '8099')
|
||||||
|
|
||||||
|
shell_pw = (getenv('SHELL_PW', 'dummy'))
|
||||||
|
|
||||||
|
|
||||||
# storage settings
|
# storage settings
|
||||||
from scopes.storage.db.postgres import StorageFactory
|
from scopes.storage.db.postgres import StorageFactory
|
||||||
dbengine = 'postgresql+psycopg'
|
dbengine = 'postgresql+psycopg'
|
||||||
|
|
7
inst/loops/runserver.sh
Executable file
7
inst/loops/runserver.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
# inst/loops/runserver.sh
|
||||||
|
|
||||||
|
# use environment variables for instance-specific configuration:
|
||||||
|
#ZOPE_CONF=zope.conf
|
||||||
|
#SERVER_PORT=8099
|
||||||
|
|
||||||
|
python -c "from loops.server.main import main; main()"
|
8
inst/loops/zshell.sh
Executable file
8
inst/loops/zshell.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
# inst/loops/zshell.sh
|
||||||
|
|
||||||
|
# use environment variables for instance-specific configuration:
|
||||||
|
#ZOPE_CONF=zope.conf
|
||||||
|
#LOOPS_PATH=sites/mysite
|
||||||
|
|
||||||
|
python -ic "from loops.server import psu; psu.setup()"
|
||||||
|
|
1
loops/server/__init__.py
Normal file
1
loops/server/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# loops.server
|
|
@ -1,6 +1,10 @@
|
||||||
# loops/inst/loops/main.py
|
# loops.server.main
|
||||||
|
|
||||||
|
# call main() for starting a loops server process
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# module aliases - should be moved to loops.server.aliases
|
||||||
from zope.securitypolicy import securitymap
|
from zope.securitypolicy import securitymap
|
||||||
sys.modules['zope.app.securitypolicy.securitymap'] = securitymap
|
sys.modules['zope.app.securitypolicy.securitymap'] = securitymap
|
||||||
|
|
||||||
|
@ -12,8 +16,11 @@ def run(app, config):
|
||||||
#print(f'Serving on port {port}.')
|
#print(f'Serving on port {port}.')
|
||||||
waitress.serve(app, port=port)
|
waitress.serve(app, port=port)
|
||||||
|
|
||||||
|
def main():
|
||||||
if __name__ == '__main__':
|
|
||||||
import config
|
import config
|
||||||
|
zope_conf = getattr(config, 'zope_conf', 'zope.conf')
|
||||||
app = getWSGIApplication('zope.conf')
|
app = getWSGIApplication('zope.conf')
|
||||||
run(app, config)
|
run(app, config)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
289
loops/server/psu.py
Normal file
289
loops/server/psu.py
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
# loops.server.psu - python shell utilities
|
||||||
|
# use this from a Python command line (in a loops / bluebrem virtual environment).
|
||||||
|
#
|
||||||
|
# then:
|
||||||
|
#
|
||||||
|
# from loops.server import psu
|
||||||
|
# import config
|
||||||
|
# psu.setup('path/to/loopsRoot', 'zope-0.conf')
|
||||||
|
# obj = psu.byuid('578457950')
|
||||||
|
#
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
from transaction import commit, abort
|
||||||
|
from zope.app import wsgi
|
||||||
|
from zope.app.authentication.principalfolder import Principal
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from zope.catalog.interfaces import ICatalog
|
||||||
|
from zope.component.hooks import setSite
|
||||||
|
from zope.container.contained import ObjectAddedEvent, ObjectRemovedEvent
|
||||||
|
from zope.copypastemove.interfaces import IContainerItemRenamer
|
||||||
|
from zope import component
|
||||||
|
from zope.event import notify
|
||||||
|
from zope.exceptions.interfaces import DuplicationError
|
||||||
|
from zope.interface import Interface
|
||||||
|
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
|
||||||
|
from zope.publisher.browser import TestRequest as BaseTestRequest
|
||||||
|
from zope.security.management import getInteraction, newInteraction, endInteraction
|
||||||
|
#from zope.pluggableauth.plugins.principalfolder import PrincipalInfo
|
||||||
|
|
||||||
|
from cybertools.util.date import date2TimeStamp, strptime
|
||||||
|
from cybertools.util.jeep import Jeep
|
||||||
|
from loops.common import adapted, baseObject
|
||||||
|
from loops.util import getObjectForUid, getUidForObject, getCatalog, reindex
|
||||||
|
|
||||||
|
os.environ['NLS_LANG'] = 'German_Germany.UTF8'
|
||||||
|
|
||||||
|
try:
|
||||||
|
import config
|
||||||
|
except ImportError:
|
||||||
|
print('*** config file could not be loaded!')
|
||||||
|
config = None
|
||||||
|
|
||||||
|
|
||||||
|
sc = Jeep() # shortcuts
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
def closeConnection():
|
||||||
|
if conn is not None:
|
||||||
|
print('closing ZODB connection...')
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def setup(zope_conf=None, loopsRootPath='sites/loops', config=config):
|
||||||
|
if zope_conf is None:
|
||||||
|
zope_conf = getattr(config, 'zope_conf', 'zope.conf')
|
||||||
|
if loopsRootPath is None:
|
||||||
|
loopsRootPath = getattr(config, 'loops_path', None)
|
||||||
|
global conn, root, sm, smdefault, intids, pau, loopsRoot, sc
|
||||||
|
print(f'opening ZODB connection... - conf: {zope_conf}, path: {loopsRootPath}')
|
||||||
|
conn = wsgi.config(zope_conf).open()
|
||||||
|
atexit.register(closeConnection)
|
||||||
|
root = conn.root()['Application']
|
||||||
|
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
|
||||||
|
input('Enter manager password: '))
|
||||||
|
login(Principal(user, password, u'Manager'))
|
||||||
|
loopsRoot = root
|
||||||
|
if loopsRootPath is not None:
|
||||||
|
for name in loopsRootPath.split('/'):
|
||||||
|
if name:
|
||||||
|
loopsRoot = loopsRoot[name]
|
||||||
|
sc.concepts = loopsRoot['concepts']
|
||||||
|
for name in ('standard', 'hasType',):
|
||||||
|
sc[name] = sc.concepts[name]
|
||||||
|
|
||||||
|
|
||||||
|
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, docommit=True):
|
||||||
|
obj = container.get(name)
|
||||||
|
if obj is None:
|
||||||
|
print('*** Object', name, 'not found!')
|
||||||
|
return
|
||||||
|
notifyRemoved(obj)
|
||||||
|
del container[name]
|
||||||
|
if docommit:
|
||||||
|
commit()
|
||||||
|
|
||||||
|
def rename(container, old, new, docommit=True):
|
||||||
|
obj = container.get(old)
|
||||||
|
if obj is None:
|
||||||
|
print('*** Object', old, 'not found!')
|
||||||
|
return
|
||||||
|
renamer = IContainerItemRenamer(container)
|
||||||
|
if new != old:
|
||||||
|
try:
|
||||||
|
renamer.renameItem(old, new)
|
||||||
|
except DuplicationError:
|
||||||
|
print('*** Object', new, 'already exists!')
|
||||||
|
# container[new] = obj
|
||||||
|
# notifyAdded(obj)
|
||||||
|
notifyModification(obj)
|
||||||
|
if docommit:
|
||||||
|
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)
|
||||||
|
|
||||||
|
# startup, 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)
|
||||||
|
if info.get('errors'):
|
||||||
|
print('*** errors: %i.' % info.error)
|
||||||
|
commit()
|
||||||
|
return fct(obj, info)
|
||||||
|
|
||||||
|
def finish(info):
|
||||||
|
print('*** count: %i.' % info.count)
|
||||||
|
if info.get('updated'):
|
||||||
|
print('*** updated: %i.' % info.updated)
|
||||||
|
if info.get('errors'):
|
||||||
|
print('*** errors: %i.' % info.error)
|
||||||
|
commit()
|
||||||
|
|
||||||
|
def stop_condition(info):
|
||||||
|
stop = info.get('stop')
|
||||||
|
return stop is not None and info.count > stop
|
||||||
|
|
||||||
|
def loop(message, objects, fct, **kw):
|
||||||
|
def _fct(obj, info):
|
||||||
|
params = info.get('fctparams', {})
|
||||||
|
fct(obj, info, **params)
|
||||||
|
info = startup(message, **kw)
|
||||||
|
for obj in objects:
|
||||||
|
update(_fct, obj, info)
|
||||||
|
if stop_condition(info):
|
||||||
|
break
|
||||||
|
finish(info)
|
||||||
|
|
||||||
|
|
||||||
|
# indexing
|
||||||
|
|
||||||
|
def reindex_objects(objs, **kw):
|
||||||
|
catalog = getCatalog(objs[0])
|
||||||
|
def do_reindex(obj, info):
|
||||||
|
util.reindex(obj, catalog)
|
||||||
|
loop('reindex %s objects' % len(objs), objs, do_reindex, **kw)
|
||||||
|
|
||||||
|
def reindex_all(root, step=1000):
|
||||||
|
catalog = getCatalog(root)
|
||||||
|
def _updateObj(params, info):
|
||||||
|
uid, obj = params
|
||||||
|
for index in catalog.values():
|
||||||
|
index.index_doc(uid, obj)
|
||||||
|
info = startup('Indexing all objects', step=step)
|
||||||
|
for uid, obj in catalog._visitSublocations():
|
||||||
|
update(_updateObj, (uid, obj), info)
|
||||||
|
finish(info)
|
||||||
|
|
||||||
|
|
||||||
|
# some common repair tasks
|
||||||
|
|
||||||
|
def get_type_instances(name):
|
||||||
|
return sc.concepts[name].getChildren([sc.hasType])
|
||||||
|
|
||||||
|
def notify_modification(c, info):
|
||||||
|
notifyModification(c)
|
||||||
|
|
||||||
|
def update_type_instances(**kw):
|
||||||
|
objs = get_type_instances(kw.pop('type'))
|
||||||
|
loop('Notify Type Instances', objs, notify_modification, **kw)
|
||||||
|
|
||||||
|
def update_type_instances_title_from_adapted(**kw):
|
||||||
|
def update_type_title_from_adapted(c, info):
|
||||||
|
c.title = adapted(c).title
|
||||||
|
notifyModification(c)
|
||||||
|
objs = get_type_instances(kw.pop('type'))
|
||||||
|
loop('Update Type Instances Title', objs, update_type_title_from_adapted, **kw)
|
||||||
|
|
||||||
|
def removeRecords(container, **kw):
|
||||||
|
"""Remove records from container selected by the criteria given."""
|
||||||
|
def remove(obj, info):
|
||||||
|
notifyRemoved(obj)
|
||||||
|
del container[obj.__name__]
|
||||||
|
info = startup('Remove records', **kw)
|
||||||
|
date = kw.pop('date', None)
|
||||||
|
if date:
|
||||||
|
kw['timeFromTo'] = (
|
||||||
|
date2TimeStamp(strptime(date + ' 00:00:00')),
|
||||||
|
date2TimeStamp(strptime(date + ' 23:59:59')))
|
||||||
|
loop('Remove records', container.query(**kw), remove, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue