source: products/quintagroup.analytics/trunk/quintagroup/analytics/browser/views.py @ 3464

Last change on this file since 3464 was 3464, checked in by kroman0, 12 years ago

Fixed syntax for python2.4

  • Property svn:eol-style set to native
File size: 20.3 KB
Line 
1from Acquisition import aq_base
2from zope.component import getUtility, getMultiAdapter, queryMultiAdapter
3
4from OFS.interfaces import IPropertyManager
5from Products.Five.browser import BrowserView
6from Products.CMFCore.utils import getToolByName
7from Products.CMFCore.interfaces import IFolderish
8from Products.Archetypes.interfaces import IBaseFolder
9try:
10    from plone.portlets.interfaces import IPortletManager
11    from plone.portlets.interfaces import IPortletAssignmentMapping
12    from plone.portlets.interfaces import ILocalPortletAssignmentManager
13except ImportError:
14    IPortletManager = lambda assignment: {}
15    IPortletAssignmentMapping = lambda assignment: {}
16    ILocalPortletAssignmentManager = lambda assignment: {}
17try:
18    from plone.portlets.interfaces import IPortletAssignmentSettings
19    IPortletAssignmentSettings
20except ImportError:
21    # Before plon4 we don't have an annotation storage for settings.
22    IPortletAssignmentSettings = lambda assignment: {}
23
24from GChartWrapper import VerticalBarStack
25from quintagroup.analytics.config import COLORS, OTHER_TYPES, NO_WF_BIND
26from quintagroup.analytics import QuintagroupAnalyticsMessageFactory as _
27
28MENUEITEMS = [{'href':'qa_overview', 'content':_('Overview')},
29              {'href':'ownership_by_type', 'content':_('Ownership by type')},
30              {'href':'ownership_by_state', 'content':_('Ownership by state')},
31              {'href':'type_by_state', 'content':_('Types by state')},
32              {'href':'portlets_stats', 'content':_('Portlets stats')},
33              {'href':'legacy_portlets', 'content':_('Legacy portlets')},
34              {'href':'properties_stats', 'content':_('Properties stats')}]
35
36
37class AnalyticsBaseView(BrowserView):
38    def analiticsNavigation(self):
39        return MENUEITEMS
40
41
42class OwnershipByType(AnalyticsBaseView):
43    MAX = 10
44
45    def __init__(self, context, request):
46        self.context = context
47        self.request = request
48        self.cat = getToolByName(self.context, 'portal_catalog')
49        self.users = None
50        self.total = None
51        self.types = None
52        self.data = {}
53
54    def getUsers(self):
55        if self.users is None:
56            index = self.cat._catalog.getIndex('Creator')
57            data = {}
58            for k in index._index.keys():
59                haslen = hasattr(index._index[k], '__len__')
60                if haslen:
61                    data[k] = len(index._index[k])
62                else:
63                    data[k] = 1
64            data = data.items()
65            data.sort(lambda a, b: a[1] - b[1])
66            data.reverse()
67            data = data[:self.MAX]
68            self.users = [i[0] for i in data]
69            self.total = [i[1] for i in data]
70        return self.users
71
72    def getTypes(self, all=False):
73        if self.types is None:
74            index = self.cat._catalog.getIndex('portal_type')
75            data = {}
76            for k in index._index.keys():
77                if not k:
78                    continue
79                haslen = hasattr(index._index[k], '__len__')
80                if haslen:
81                    data[k] = len(index._index[k])
82                else:
83                    data[k] = 1
84            data = data.items()
85            data.sort(lambda a, b: a[1] - b[1])
86            data.reverse()
87            self.types = [i[0] for i in data]
88        return all and self.types or self.types[:self.MAX]
89
90    def getContent(self, type_):
91        if type_ not in self.data:
92            data = self.data[type_] = []
93            for user in self.getUsers():
94                res = self.cat(portal_type=type_, Creator=user)
95                l = len(res)
96                if l == 0:
97                    data.append(0)
98                else:
99                    data.append(l)
100        return self.data[type_]
101
102    def getTotal(self):
103        return self.total
104
105    def getChart(self):
106        data = []
107        types = self.getTypes()
108        for type_ in types:
109            data.append(self.getContent(type_))
110        other = [self.getContent(t) for t in self.getTypes(
111                                                    all=True)[self.MAX:]]
112        if other:
113            data.append([sum(l) for l in zip(*other)])
114        max_value = max(self.getTotal())
115        chart = VerticalBarStack(data, encoding='text')
116        types = other and types + OTHER_TYPES or types
117        chart.title(_('Content ownership by type')).legend(*(types))
118        chart.bar('a', 10, 0).legend_pos("b")
119        chart.color(*COLORS)
120        chart.size(800, 375).scale(0, max_value).axes('xy').label(*self.users)
121        chart.axes.type("y")
122        chart.axes.range(0, 0, max_value)
123        return chart.img()
124
125
126class OwnershipByState(AnalyticsBaseView):
127    MAX = 10
128
129    def __init__(self, context, request):
130        self.context = context
131        self.request = request
132        self.cat = getToolByName(self.context, 'portal_catalog')
133        self.users = None
134        self.states = None
135        self.total = None
136        self.data = {}
137
138    def getUsers(self):
139        if self.users is None:
140            index = self.cat._catalog.getIndex('Creator')
141            data = {}
142            for k in index._index.keys():
143                haslen = hasattr(index._index[k], '__len__')
144                if haslen:
145                    data[k] = len(index._index[k])
146                else:
147                    data[k] = 1
148            data = data.items()
149            data.sort(lambda a, b: a[1] - b[1])
150            data.reverse()
151            data = data[:self.MAX]
152            self.users = [i[0] for i in data]
153            self.total = [i[1] for i in data]
154        return self.users
155
156    def getStates(self):
157        if self.states is None:
158            index = self.cat._catalog.getIndex('review_state')
159            data = {}
160            for k in index._index.keys():
161                haslen = hasattr(index._index[k], '__len__')
162                if haslen:
163                    data[k] = len(index._index[k])
164
165                else:
166                    data[k] = 1
167
168            data = data.items()
169            data.sort(lambda a, b: a[1] - b[1])
170            data.reverse()
171            self.states = [i[0] for i in data]
172        return self.states
173
174    def getContent(self, type_):
175        if type_ not in self.data:
176            if NO_WF_BIND not in self.data:
177                self.data[NO_WF_BIND] = self.getTotal()
178            data = self.data[type_] = []
179            for user in self.getUsers():
180                res = self.cat(review_state=type_, Creator=user)
181                l = len(res)
182                if l == 0:
183                    data.append(0)
184                else:
185                    data.append(l)
186            if len(data) > 0:
187                self.data[NO_WF_BIND] = map(lambda t, d: t - d,
188                                            self.data[NO_WF_BIND], data)
189        return self.data[type_]
190
191    def getNoWFContentTitle(self):
192        return NO_WF_BIND
193
194    def getNoWFContent(self):
195        return self.getContent(NO_WF_BIND)
196
197    def getTotal(self):
198        if self.total is None:
199            self.getUsers()
200        return self.total
201
202    def getChart(self):
203        data = []
204        for state in self.getStates():
205            data.append(self.getContent(state))
206        data.append(self.getNoWFContent())
207        max_value = max(self.getTotal())
208        chart = VerticalBarStack(data, encoding='text')
209        title = _('Content ownership by state')
210        chart.title(title).legend(*self.states + [NO_WF_BIND])
211        chart.bar('a', 10, 0).legend_pos("b")
212        chart.color(*COLORS)
213        chart.size(800, 375).scale(0, max_value).axes('xy').label(*self.users)
214        chart.axes.type("y")
215        chart.axes.range(0, 0, max_value)
216        return chart.img()
217
218
219class TypeByState(AnalyticsBaseView):
220    MAX = 10
221
222    def __init__(self, context, request):
223        self.context = context
224        self.request = request
225        self.cat = getToolByName(self.context, 'portal_catalog')
226        self.types = None
227        self.states = None
228        self.total = None
229        self.data = {}
230
231    def getTypes(self):
232        if self.types is None:
233            index = self.cat._catalog.getIndex('portal_type')
234            data = {}
235            for k in index._index.keys():
236                if not k:
237                    continue
238                haslen = hasattr(index._index[k], '__len__')
239                if haslen:
240                    data[k] = len(index._index[k])
241                else:
242                    data[k] = 1
243            data = data.items()
244            data.sort(lambda a, b: a[1] - b[1])
245            data.reverse()
246            self.types = [i[0] for i in data[:self.MAX]]
247            self.total = [i[1] for i in data[:self.MAX]]
248        return self.types
249
250    def getStates(self):
251        if self.states is None:
252            index = self.cat._catalog.getIndex('review_state')
253            data = {}
254            for k in index._index.keys():
255                haslen = hasattr(index._index[k], '__len__')
256                if haslen:
257                    data[k] = len(index._index[k])
258                else:
259                    data[k] = 1
260            data = data.items()
261            data.sort(lambda a, b: a[1] - b[1])
262            data.reverse()
263            self.states = [i[0] for i in data]
264        return self.states
265
266    def getContent(self, state):
267        if state not in self.data:
268            if NO_WF_BIND not in self.data:
269                self.data[NO_WF_BIND] = self.getTotal()
270            data = self.data[state] = []
271            for type_ in self.getTypes():
272                res = self.cat(portal_type=type_, review_state=state)
273                l = len(res)
274                if l == 0:
275                    data.append(0)
276                else:
277                    data.append(l)
278            if len(data) > 0:
279                self.data[NO_WF_BIND] = map(lambda t, d: t - d,
280                                            self.data[NO_WF_BIND], data)
281        return self.data[state]
282
283    def getTotal(self):
284        if self.total is None:
285            self.getTypes()
286        return self.total
287
288    def getNoWFContentTitle(self):
289        return NO_WF_BIND
290
291    def getNoWFContent(self):
292        return self.getContent(NO_WF_BIND)
293
294    def getChart(self):
295        data = []
296        for state in self.getStates():
297            data.append(self.getContent(state))
298        data.append(self.getContent(NO_WF_BIND))
299        max_value = max(self.getTotal())
300        chart = VerticalBarStack(data, encoding='text')
301        chart.title(_('Content type by state')).legend(
302            *self.states + [NO_WF_BIND])
303        chart.bar('a', 10, 0).legend_pos("b")
304        chart.color(*COLORS)
305        chart.size(800, 375).scale(0, max_value).axes('xy').label(*self.types)
306        chart.axes.type("y")
307        chart.axes.range(0, 0, max_value)
308        return chart.img()
309
310
311class LegacyPortlets(AnalyticsBaseView):
312    def __init__(self, context, request):
313        self.context = context
314        self.request = request
315        self.total = None
316        self.DEBUG = False
317        self.expressions = set()
318
319    def _getInfo(self, obj):
320        href = obj.absolute_url()
321        path = '/'.join(obj.getPhysicalPath())
322        info = {
323            'path': path,
324            'href': href,
325            'left_slots': None,
326            'right_slots': None,
327        }
328        if IPropertyManager.providedBy(obj):
329            obj = aq_base(obj)
330            if obj.hasProperty('left_slots'):
331                info['left_slots'] = obj.getProperty('left_slots')
332                self.expressions = self.expressions.union(
333                                            set(info['left_slots']))
334            if obj.hasProperty('right_slots'):
335                info['right_slots'] = obj.getProperty('right_slots')
336                self.expressions = self.expressions.union(
337                                            set(info['right_slots']))
338        return info
339
340    def _walk(self, obj, level=-1):
341        yield self._getInfo(obj)
342        if level != 0 and (IFolderish.providedBy(obj) \
343                       or IBaseFolder.providedBy(obj)):
344            for v in obj.contentValues():
345                for i in self._walk(v, level - 1):
346                    yield i
347
348    def getPortlets(self):
349        level = self.request.form.get('level', 1)
350        try:
351            level = level and int(level) or 1
352        except ValueError:
353            level = 1
354        infos = []
355        for i in self._walk(self.context, level):
356            if self.DEBUG or i['left_slots'] is not None \
357                          or i['right_slots'] is not None:
358                infos.append(i)
359        self.total = len(infos)
360        return infos
361
362    def getTotal(self):
363        return self.total
364
365    def getAllPortletExpressions(self):
366        exprs = []
367        for name in self.expressions:
368            name = name.split('|')[0]
369            if not '/macros/' in name:
370                name = name.split('/')[-1]
371            name = name.strip()
372            if name not in exprs:
373                exprs.append(name)
374        exprs.sort()
375        return exprs
376
377
378class PropertiesStats(AnalyticsBaseView):
379    def __init__(self, context, request):
380        self.context = context
381        self.request = request
382        self.total = None
383        self.DEBUG = False
384        self.expressions = set()
385        self.proplist = []
386        self.propname = self.request.form.get('propname') or ""
387
388    def _getInfo(self, obj):
389
390        href = obj.absolute_url()
391        path = '/'.join(obj.getPhysicalPath())
392        info = {
393            'path': path,
394            'href': href,
395            'slots': None,
396        }
397        if IPropertyManager.providedBy(obj):
398            obj = aq_base(obj)
399            self.proplist.extend(
400                    [i for i in obj.propertyIds() if i not in self.proplist])
401            if obj.hasProperty(self.propname):
402                info['slots'] = obj.getProperty(self.propname)
403                if isinstance(info['slots'], int):
404                    info['slots'] = str(info['slots'])
405                if not isinstance(info['slots'], basestring):
406                    self.expressions = self.expressions.union(
407                                                set(info['slots']))
408                else:
409                    self.expressions = self.expressions.union(
410                                                set([info['slots']]))
411        return info
412
413    def _walk(self, obj, level=-1):
414        yield self._getInfo(obj)
415        if level != 0 and (IFolderish.providedBy(obj) \
416                       or IBaseFolder.providedBy(obj)):
417            for v in obj.contentValues():
418                for i in self._walk(v, level - 1):
419                    yield i
420
421    def getPropsList(self):
422        level = self.request.form.get('level', 1)
423        try:
424            level = level and int(level) or 1
425        except ValueError:
426            level = 1
427        infos = []
428        for i in self._walk(self.context, level):
429            if self.DEBUG or i['slots'] is not None:
430                infos.append(i)
431        self.total = len(infos)
432        return infos
433
434    def getTotal(self):
435        return self.total
436
437    def getAllPortletExpressions(self):
438        exprs = []
439        for name in self.expressions:
440            name = name.strip()
441            if name not in exprs:
442                exprs.append(name)
443        #exprs.sort()
444        return exprs
445
446
447class PortletsStats(AnalyticsBaseView):
448    def __init__(self, context, request):
449        self.context = context
450        self.request = request
451        self.total = None
452        self.DEBUG = False
453        self.expressions = set()
454        self.proplist = []
455        self.propname = self.request.form.get('propname') or ""
456
457    def getAssignmentMappingUrl(self, context, manager):
458        baseUrl = str(getMultiAdapter((context, self.request),
459                                      name='absolute_url'))
460        return '%s/++contextportlets++%s' % (baseUrl, manager.__name__)
461
462    def getAssignmentsForManager(self, context, manager):
463        assignments = getMultiAdapter((context, manager),
464                                      IPortletAssignmentMapping)
465        return assignments.values()
466
467    def getPortletsMapping(self, context):
468        leftcolumn = getUtility(IPortletManager, name=u'plone.leftcolumn',
469                                context=context)
470        rightcolumn = getUtility(IPortletManager, name=u'plone.rightcolumn',
471                                 context=context)
472        leftmapping = getMultiAdapter((context, leftcolumn,),
473                                      IPortletAssignmentMapping)
474        rightmapping = getMultiAdapter((context, rightcolumn,),
475                                       IPortletAssignmentMapping)
476        return (leftmapping, rightmapping)
477
478    def getLocalPortletsManager(self, context):
479        leftcolumn = getUtility(IPortletManager, name='plone.leftcolumn',
480                                context=context)
481        rightcolumn = getUtility(IPortletManager, name='plone.rightcolumn',
482                                 context=context)
483        leftmanager = getMultiAdapter((context, leftcolumn,),
484                                      ILocalPortletAssignmentManager)
485        rightmanager = getMultiAdapter((context, rightcolumn,),
486                                       ILocalPortletAssignmentManager)
487        return (leftmanager, rightmanager)
488
489    def getPortletsManager(self, context):
490        left = getUtility(IPortletManager, name='plone.leftcolumn',
491                          context=context)
492        right = getUtility(IPortletManager, name='plone.rightcolumn',
493                           context=context)
494        return (left, right)
495
496    def portlets_for_assignments(self, assignments, manager, base_url):
497        data = []
498        for idx in range(len(assignments)):
499            name = assignments[idx].__name__
500
501            editview = queryMultiAdapter(
502                (assignments[idx], self.request), name='edit', default=None)
503
504            if editview is None:
505                editviewName = ''
506            else:
507                editviewName = '%s/%s/edit' % (base_url, name)
508
509            settings = IPortletAssignmentSettings(assignments[idx])
510
511            data.append({
512                'title': assignments[idx].title,
513                'editview': editviewName,
514                'visible': settings.get('visible', True),
515                })
516        return data
517
518    def getPortlets(self, context, mapping, manager):
519        #import pdb; pdb.set_trace()
520        return mapping.keys()
521
522    def _getInfo(self, obj):
523        href = obj.absolute_url()
524        path = '/'.join(obj.getPhysicalPath())
525        info = {
526            'path': path,
527            'href': href,
528            'left_slots': None,
529            'right_slots': None,
530        }
531        left, right = self.getPortletsManager(obj)
532        #leftmapping, rightmapping = self.getPortletsMapping(obj)
533        #leftmanager, rightmanager = self.getLocalPortletsManager(obj)
534        #info['left_slots'] = self.getPortlets(obj, leftmapping, leftmanager)
535        #info['right_slots'] = self.getPortlets(obj, rightmapping,rightmanager)
536        lass = self.getAssignmentsForManager(obj, left)
537        rass = self.getAssignmentsForManager(obj, right)
538        lurl = self.getAssignmentMappingUrl(obj, left)
539        rurl = self.getAssignmentMappingUrl(obj, right)
540        plass = self.portlets_for_assignments(lass, left, lurl)
541        prass = self.portlets_for_assignments(rass, right, rurl)
542        #print obj, plass, prass
543        info['left_slots'] = plass  # [i['title'] for i in plass]
544        info['right_slots'] = prass  # [i['title'] for i in prass]
545        return info
546
547    def _walk(self, obj, level=-1):
548        try:
549            yield self._getInfo(obj)
550        except:
551            pass
552        if level != 0 and (IFolderish.providedBy(obj) \
553                       or IBaseFolder.providedBy(obj)):
554            for v in obj.contentValues():
555                for i in self._walk(v, level - 1):
556                    yield i
557
558    def getPropsList(self):
559        level = self.request.form.get('level', 1)
560        try:
561            level = level and int(level) or 1
562        except ValueError:
563            level = 1
564        infos = []
565        for i in self._walk(self.context, level):
566            if self.DEBUG or i['left_slots'] is not None \
567                          or i['right_slots'] is not None:
568                infos.append(i)
569        self.total = len(infos)
570        return infos
571
572    def getTotal(self):
573        return self.total
574
575    def getAllPortletExpressions(self):
576        exprs = []
577        for name in self.expressions:
578            name = name.strip()
579            if name not in exprs:
580                exprs.append(name)
581        #exprs.sort()
582        return exprs
Note: See TracBrowser for help on using the repository browser.