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

Last change on this file since 3666 was 3666, checked in by kroman0, 11 years ago

flake8

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