work in progress: access tracking - loading access log basically OK
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2964 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
bb6a3b3d7b
commit
bf453ace5a
6 changed files with 189 additions and 39 deletions
|
@ -22,10 +22,13 @@ Recording changes to loops objects.
|
|||
$Id$
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
import transaction
|
||||
from zope.app.publication.interfaces import IEndRequestEvent
|
||||
from zope.interface import Interface
|
||||
from zope.interface import Interface, implements
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.component import adapter
|
||||
from zope.security.proxy import removeSecurityProxy
|
||||
|
@ -35,13 +38,14 @@ from cybertools.tracking.btree import Track, getTimeStamp
|
|||
from cybertools.tracking.interfaces import ITrack
|
||||
from cybertools.tracking.logfile import Logger, loggers
|
||||
from loops.interfaces import ILoopsObject
|
||||
from loops.organize.party import getPersonForUser
|
||||
from loops.security.common import getCurrentPrincipal
|
||||
from loops.organize.tracking.base import BaseRecordManager
|
||||
from loops import util
|
||||
|
||||
|
||||
# logging
|
||||
|
||||
logfile_option = 'organize.tracking.logfile'
|
||||
request_key = 'loops.organize.tracking.access'
|
||||
loggers_key = 'loops.access'
|
||||
|
||||
fields = {
|
||||
'001': ('principal', 'node', 'target', 'view', 'params'),
|
||||
|
@ -71,11 +75,12 @@ def logAccess(event):
|
|||
logger = loggers.get(loggers_key)
|
||||
if not logger:
|
||||
options = IOptions(context.getLoopsRoot())
|
||||
logfile = options('organize.tracking.logfile')
|
||||
logfile = options(logfile_option)
|
||||
if not logfile:
|
||||
return
|
||||
path = os.path.join(util.getVarDirectory(), logfile[0])
|
||||
logger = loggers[loggers_key] = Logger(loggers_key, path)
|
||||
fn = logfile[0]
|
||||
path = os.path.join(util.getVarDirectory(), fn)
|
||||
logger = loggers[fn] = Logger(fn, path)
|
||||
logger.log(marshall(data))
|
||||
|
||||
|
||||
|
@ -85,3 +90,88 @@ def marshall(data):
|
|||
for key in fields[version]:
|
||||
values.append(data.get(key) or '')
|
||||
return ';'.join(values)
|
||||
|
||||
|
||||
# record manager
|
||||
|
||||
class AccessRecordManager(BaseRecordManager):
|
||||
|
||||
storageName = 'access'
|
||||
|
||||
def __init__(self, context, request):
|
||||
self.context = context
|
||||
self.request = request
|
||||
|
||||
@Lazy
|
||||
def logfile(self):
|
||||
value = self.options(logfile_option)
|
||||
return value and value[0] or None
|
||||
|
||||
@Lazy
|
||||
def valid(self):
|
||||
return self.storage is not None and self.logfile
|
||||
|
||||
def loadRecordsFromLog(self):
|
||||
if not self.valid:
|
||||
return
|
||||
fn = self.logfile
|
||||
path = os.path.join(util.getVarDirectory(), fn)
|
||||
logger = loggers.get(fn)
|
||||
if not logger:
|
||||
logger = loggers[fn] = Logger(fn, path)
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
lf = open(path, 'r')
|
||||
for idx, line in enumerate(lf):
|
||||
self.processLogRecord(idx, line)
|
||||
lf.close()
|
||||
transaction.commit()
|
||||
logger.doRollover()
|
||||
|
||||
def processLogRecord(self, idx, line):
|
||||
if not line:
|
||||
return
|
||||
values = line.split(';')
|
||||
timeString = values.pop(0)
|
||||
version = values.pop(0)
|
||||
if version not in fields:
|
||||
logging.getLogger('AccessRecordManager').warn(
|
||||
'Undefined logging record version %r on record %i.'
|
||||
% (version, idx))
|
||||
return
|
||||
if len(values) != len(fields[version]):
|
||||
logging.getLogger('AccessRecordManager').warn(
|
||||
'Length of record %i does not match version %r.'
|
||||
% (idx, version))
|
||||
return
|
||||
data = {}
|
||||
for idx, field in enumerate(fields[version]):
|
||||
data[field] = values[idx]
|
||||
timeStamp = timeStringToTimeStamp(timeString)
|
||||
personId = self.getPersonId(data['principal'])
|
||||
taskId = data['target'] or data['node']
|
||||
existing = self.storage.query(taskId=taskId, userName=self.personId,
|
||||
timeStamp=timeStamp)
|
||||
for track in existing:
|
||||
if track.data == data: # has been recorded already
|
||||
return
|
||||
self.storage.saveUserTrack(taskId, 0, personId, data,
|
||||
timeStamp=timeStamp)
|
||||
|
||||
|
||||
class IAccessRecord(ITrack):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AccessRecord(Track):
|
||||
|
||||
implements(IAccessRecord)
|
||||
|
||||
typeName = 'AccessRecord'
|
||||
|
||||
|
||||
def timeStringToTimeStamp(timeString):
|
||||
s, decimal = timeString.split(',')
|
||||
t = time.strptime(s, '%Y-%m-%d %H:%M:%S')
|
||||
return int(time.mktime(t))
|
||||
|
|
71
organize/tracking/base.py
Normal file
71
organize/tracking/base.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#
|
||||
# Copyright (c) 2008 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
|
||||
#
|
||||
|
||||
"""
|
||||
Base class(es) for track/record managers.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
from cybertools.meta.interfaces import IOptions
|
||||
from loops.organize.party import getPersonForUser
|
||||
from loops.organize.util import getPrincipalForUserId
|
||||
from loops.security.common import getCurrentPrincipal
|
||||
from loops import util
|
||||
|
||||
|
||||
class BaseRecordManager(object):
|
||||
|
||||
context = None
|
||||
valid = True
|
||||
storageName = None
|
||||
|
||||
@Lazy
|
||||
def options(self):
|
||||
#return IOptions(self.context)
|
||||
return IOptions(self.loopsRoot)
|
||||
|
||||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.context.getLoopsRoot()
|
||||
|
||||
@Lazy
|
||||
def storage(self):
|
||||
records = self.loopsRoot.getRecordManager()
|
||||
if records is not None:
|
||||
return records.get(self.storageName)
|
||||
return None
|
||||
|
||||
@Lazy
|
||||
def personId(self):
|
||||
return self.getPersonId()
|
||||
|
||||
def getPersonId(self, userId=None):
|
||||
if userId is None:
|
||||
principal = getCurrentPrincipal()
|
||||
else:
|
||||
principal = getPrincipalForUserId(userId, context=self.context)
|
||||
if principal is not None:
|
||||
person = getPersonForUser(self.context, principal=principal)
|
||||
if person is None:
|
||||
return principal.id
|
||||
return util.getUidForObject(person)
|
||||
return None
|
||||
|
|
@ -37,24 +37,20 @@ from loops.resource import ResourceManager
|
|||
from loops.interfaces import IAssignmentEvent, IDeassignmentEvent
|
||||
from loops.interfaces import ILoopsObject
|
||||
from loops.organize.party import getPersonForUser
|
||||
from loops.organize.tracking.base import BaseRecordManager
|
||||
from loops.security.common import getCurrentPrincipal
|
||||
from loops import util
|
||||
|
||||
|
||||
class ChangeManager(object):
|
||||
class ChangeManager(BaseRecordManager):
|
||||
|
||||
context = None
|
||||
storageName = 'changes'
|
||||
|
||||
def __init__(self, context):
|
||||
if isinstance(context, (ConceptManager, ResourceManager)):
|
||||
return
|
||||
self.context = context
|
||||
|
||||
@Lazy
|
||||
def options(self):
|
||||
#return IOptions(self.context)
|
||||
return IOptions(self.loopsRoot)
|
||||
|
||||
@Lazy
|
||||
def valid(self):
|
||||
return (not (self.context is None or
|
||||
|
@ -62,27 +58,6 @@ class ChangeManager(object):
|
|||
self.personId is None)
|
||||
and self.options('organize.tracking.changes'))
|
||||
|
||||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.context.getLoopsRoot()
|
||||
|
||||
@Lazy
|
||||
def storage(self):
|
||||
records = self.loopsRoot.getRecordManager()
|
||||
if records is not None:
|
||||
return records.get('changes')
|
||||
return None
|
||||
|
||||
@Lazy
|
||||
def personId(self):
|
||||
principal = getCurrentPrincipal()
|
||||
if principal is not None:
|
||||
person = getPersonForUser(self.context, principal=principal)
|
||||
if person is None:
|
||||
return principal.id
|
||||
return util.getUidForObject(person)
|
||||
return None
|
||||
|
||||
def recordModification(self, action='modify', **kw):
|
||||
if not self.valid:
|
||||
return
|
||||
|
@ -117,10 +92,6 @@ class ChangeRecord(Track):
|
|||
def recordModification(obj, event):
|
||||
ChangeManager(obj).recordModification()
|
||||
|
||||
#@adapter(ILoopsObject, IObjectCreatedEvent)
|
||||
#def recordCreation(obj, event):
|
||||
# ChangeManager(obj).recordModification('create')
|
||||
|
||||
@adapter(ILoopsObject, IObjectAddedEvent)
|
||||
def recordAdding(obj, event):
|
||||
ChangeManager(obj).recordModification('add')
|
||||
|
|
|
@ -26,6 +26,13 @@
|
|||
|
||||
<zope:subscriber handler="loops.organize.tracking.access.logAccess" />
|
||||
|
||||
<browser:page
|
||||
name="load_access_records"
|
||||
for="loops.interfaces.ILoops"
|
||||
class="loops.organize.tracking.access.AccessRecordManager"
|
||||
attribute="loadRecordsFromLog"
|
||||
permission="zope.Public" />
|
||||
|
||||
<zope:adapter factory="loops.organize.tracking.setup.SetupManager"
|
||||
name="organize.tracking" />
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ from zope.interface import implements, Interface
|
|||
|
||||
from cybertools.tracking.btree import TrackingStorage
|
||||
from loops.organize.tracking.change import ChangeRecord
|
||||
from loops.organize.tracking.access import AccessRecord
|
||||
from loops.setup import SetupManager as BaseSetupManager
|
||||
|
||||
|
||||
|
@ -36,3 +37,5 @@ class SetupManager(BaseSetupManager):
|
|||
records = self.context.getRecordManager()
|
||||
changes = self.addObject(records, TrackingStorage, 'changes',
|
||||
trackFactory=ChangeRecord)
|
||||
access = self.addObject(records, TrackingStorage, 'access',
|
||||
trackFactory=AccessRecord)
|
||||
|
|
|
@ -84,6 +84,14 @@ def getInternalPrincipal(id, context=None):
|
|||
raise PrincipalLookupError(id)
|
||||
|
||||
|
||||
def getPrincipalForUserId(id, context=None):
|
||||
auth = component.getUtility(IAuthentication, context=context)
|
||||
try:
|
||||
return auth.getPrincipal(id)
|
||||
except PrincipalLookupError:
|
||||
return None
|
||||
|
||||
|
||||
def getTrackingStorage(obj, name):
|
||||
records = obj.getLoopsRoot().getRecordManager()
|
||||
if records is not None:
|
||||
|
|
Loading…
Add table
Reference in a new issue