From bc538f2fd77438c162c88623d31f2a02785be667 Mon Sep 17 00:00:00 2001 From: helmutm Date: Sat, 6 Dec 2008 10:06:58 +0000 Subject: [PATCH] provide 'recent changes' view git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3031 fd906abe-77d9-0310-91a1-e0d9ade77398 --- CHANGES.txt | 11 ++++- browser/common.py | 13 ++++++ browser/node.py | 6 ++- organize/tracking/README.txt | 25 +++++++++++ organize/tracking/configure.zcml | 6 +++ organize/tracking/report.pt | 27 +++++++++++- organize/tracking/report.py | 73 ++++++++++++++++++++++++++++++-- util.py | 2 + 8 files changed, 155 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed72985..b3ded28 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,10 +6,17 @@ $Id$ 0.9 --- +New features + - basic job management: a job executor view calls job managers specified by loops root option ``organize.job.managers`` - allow ``__getitem__`` on Loops and ViewManager, this is a prerequisite for using virtual hosts over more than one path element (e.g. leading to - views/home) on protected sites; this also allows calling of job processiong - views via wget without login credentials + views/home) on protected sites; this also allows calling of (public) + job processiong views via wget without login credentials - add definition of loops package version (see loops/version.py) + +Bug fixes + +- use select version on file, image and media asset links + (via ``?version=this`` in the URL) diff --git a/browser/common.py b/browser/common.py index d953191..dd14e5d 100644 --- a/browser/common.py +++ b/browser/common.py @@ -160,6 +160,19 @@ class BaseView(GenericView, I18NView): # allow for having a separate object the view acts upon return self.context + @Lazy + def viewAnnotations(self): + return self.request.annotations.get('loops.view', {}) + + @Lazy + def node(self): + return self.viewAnnotations.get('node') + + @Lazy + def nodeView(self): + ann = self.request.annotations.get('loops.view', {}) + return self.viewAnnotations.get('nodeView') + @Lazy def params(self): result = {} diff --git a/browser/node.py b/browser/node.py index 492f644..1fa6214 100644 --- a/browser/node.py +++ b/browser/node.py @@ -76,6 +76,8 @@ class NodeView(BaseView): def __init__(self, context, request): super(NodeView, self).__init__(context, request) + viewAnnotations = request.annotations.setdefault('loops.view', {}) + viewAnnotations['nodeView'] = self viewConfig = getViewConfiguration(context, request) self.setSkin(viewConfig.get('skinName')) @@ -738,6 +740,8 @@ class NodeTraverser(ItemTraverser): component.adapts(INode) def publishTraverse(self, request, name): + viewAnnotations = request.annotations.setdefault('loops.view', {}) + viewAnnotations['node'] = self.context if self.context.nodeType == 'menu': setViewConfiguration(self.context, request) if name == '.loops': @@ -761,8 +765,6 @@ class NodeTraverser(ItemTraverser): target = self.context.target if target is not None: # remember self.context in request - viewAnnotations = request.annotations.setdefault('loops.view', {}) - viewAnnotations['node'] = self.context if request.method == 'PUT': # we have to use the target object directly return target diff --git a/organize/tracking/README.txt b/organize/tracking/README.txt index 487038c..569a9d1 100644 --- a/organize/tracking/README.txt +++ b/organize/tracking/README.txt @@ -133,6 +133,9 @@ of job control. Tracking Reports ================ +Overview (cumulative) statistics +-------------------------------- + >>> from loops.organize.tracking.report import TrackingStats >>> view = TrackingStats(home, TestRequest()) @@ -142,6 +145,28 @@ Tracking Reports >>> result['data'] [{'access': 2, 'new': 0, 'changed': 1, 'period': '...', 'count': 3}] +Recent changes +-------------- + + >>> from loops.organize.tracking.report import RecentChanges + >>> view = RecentChanges(home, TestRequest()) + >>> result = view.getData() + >>> result['macro'][4][1][u'define-macro'] + u'recent_changes' + + >>> data = result['data'] + >>> data + [] + + >>> data[0].timeStamp + u'... ...:...' + >>> data[0].object + {'url': '', 'object': , 'title': 'Change Doc 001'} + >>> data[0].user + {'url': '', 'object': , 'title': u'john'} + >>> data[0].action + 'modify' + Fin de partie ============= diff --git a/organize/tracking/configure.zcml b/organize/tracking/configure.zcml index 53d52f4..27d383b 100644 --- a/organize/tracking/configure.zcml +++ b/organize/tracking/configure.zcml @@ -42,6 +42,12 @@ class="loops.organize.tracking.report.TrackingStats" permission="zope.View" /> + + -

Statistics Report

+

Statistics Report


@@ -29,3 +30,27 @@ + + + + + + + + + + + + + + + +
TitleUserDate/TimeChange
+ +
+
diff --git a/organize/tracking/report.py b/organize/tracking/report.py index e7ce476..c36f086 100644 --- a/organize/tracking/report.py +++ b/organize/tracking/report.py @@ -28,9 +28,11 @@ from zope.cachedescriptors.property import Lazy from zope.traversing.browser import absoluteURL from zope.traversing.api import getName +from cybertools.util import format from loops.browser.common import BaseView from loops.interfaces import IResource from loops import util +from loops.util import _ report_macros = ViewPageTemplateFile('report.pt') @@ -39,6 +41,7 @@ report_macros = ViewPageTemplateFile('report.pt') class TrackingStats(BaseView): template = report_macros + title = _(u'Statistics Report') @Lazy def macro(self): @@ -112,11 +115,75 @@ class TrackingStats(BaseView): the period given. """ - def getObjectDetails(uid): - """ Listing of last n accesses and changes of the object specified by - the uid given. + def getObjectDetails(uid, period=None): + """ Listing of (last n?) accesses and changes of the object specified by + the uid given, optionally limited to the period (month) given. """ +class RecentChanges(TrackingStats): + + title = _(u'Recent Changes') + + def getData(self): + length = self.request.form.get('length', 15) + new = {} + changed = {} + result = [] + for track in self.changeRecords: + if track.data['action'] == 'add' and track.taskId not in new: + new[track.taskId] = track + result.append(track) + continue + if track.data['action'] == 'modify' and track.taskId not in changed: + sameNew = new.get(track.taskId) + if sameNew and sameNew.timeStamp > track.timeStamp - 60: + # change immediate after creation + continue + changed[track.taskId] = track + result.append(track) + continue + return dict(data=[TrackDetails(self, tr) for tr in result[:length]], + macro=self.macros['recent_changes']) + + +class TrackDetails(object): + + timeStampFormat = 'short' + + def __init__(self, view, track): + self.view = view + self.track = track + + @Lazy + def object(self): + obj = util.getObjectForUid(self.track.taskId) + node = self.view.nodeView + url = node is not None and node.getUrlForTarget(obj) or '' + return dict(object=obj, title=obj.title, url=url) + + @Lazy + def user(self): + obj = util.getObjectForUid(self.track.userName) + if obj is None: + return dict(object=None, title=userName, url='') + node = self.view.nodeView + url = node is not None and node.getUrlForTarget(obj) or '' + return dict(object=obj, title=obj.title, url=url) + + @Lazy + def action(self): + return self.track.data['action'] + + @Lazy + def timeStamp(self): + value = datetime.fromtimestamp(self.track.timeStamp) + return format.formatDate(value, 'dateTime', self.timeStampFormat, + self.view.languageInfo.language) + + def __repr__(self): + return repr(self.track) + + def formatAsMonth(d): return d.isoformat()[:7] diff --git a/util.py b/util.py index 6d6ab31..670024c 100644 --- a/util.py +++ b/util.py @@ -85,6 +85,8 @@ def toUnicode(value, encoding='UTF-8'): def getObjectForUid(uid, intIds=None): if uid == '*': # wild card return '*' + if isinstance(uid, basestring) and not uid.isdigit(): # not a valid uid + return None if intIds is None: intIds = component.getUtility(IIntIds) return intIds.getObject(int(uid))