source: products/quintagroup.analytics/branches/update_mt/quintagroup/analytics/browser/views.py @ 3090

Last change on this file since 3090 was 3090, checked in by mylan, 13 years ago

Added view for getting information about size of all objects by path

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