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

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

Fixed tests for plone 42 and pyflakes

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