source: timingandestimationplugin/branches/trac0.11-Permissions/timingandestimationplugin/reports_filter.py

Last change on this file was 7011, checked in by Russ Tyndall, 14 years ago

fixed #6076 - Changed the way I was filtering TandE report rows, so that it would work with newer genshis and newer tracs

File size: 8.2 KB
RevLine 
[4260]1from trac.web.api import ITemplateStreamFilter
2from trac.core import *
3from genshi.core import *
4from genshi.builder import tag
[6531]5try: 
6    set 
7except NameError: 
8    from sets import Set as set     # Python 2.3 fallback
9
[4260]10from genshi.filters.transform import Transformer
[4280]11import re
[7000]12import dbhelper
[4260]13
[4281]14from trac.ticket.report import ReportModule
15from trac.util.datefmt import format_datetime, format_time
16import csv
17from trac.web.api import RequestDone
18
[6790]19
20# This can go away once they fix http://genshi.edgewall.org/ticket/136
21# At that point we should use Transformer.filter
[6826]22# THIS IS STILL SOLVING PROBLEMS WELL AFTER THAT TICKET HAS BEEN CLOSED - A new ticket #290 [1000] has fixed the bug, but is
23# not the trac default yet
[6790]24# Without this (using the default filter) I was getting omitted closing tags for some tags (Based on whitespace afaict)
25class FilterTransformation(object):
26    """Apply a normal stream filter to the selection. The filter is called once
27    for each contiguous block of marked events."""
28
29    def __init__(self, filter):
30        """Create the transform.
31
32        :param filter: The stream filter to apply.
33        """
34        self.filter = filter
35
36    def __call__(self, stream):
37        """Apply the transform filter to the marked stream.
38
39        :param stream: The marked event stream to filter
40        """
41        def flush(queue):
42            if queue:
43                for event in self.filter(queue):
44                    yield event
45                del queue[:]
46
47        queue = []
48        for mark, event in stream:
49            if mark:
50                queue.append(event)
51            else:
52                for e in flush(queue):
53                    yield None,e
54                yield None,event
55        for event in flush(queue):
56            yield None,event
57
[7011]58def split_stream(stream):
59    """splits the stream based on toplevel START / END tags"""
60    cl = []
61    res = []
62    num_start=0
63    for kind, data, pos in stream:
64        cl.append((kind, data, pos))
65        if kind == Stream.START:
66            num_start = num_start+1
67        elif kind == Stream.END:
68            num_start = num_start-1
69            if num_start == 0:
70                res.append(Stream(cl))
71                cl=[]
72    if cl != []:
73        res.append(Stream(cl))
74    return res
75               
[6790]76
[4280]77billing_report_regex = re.compile("\{(?P<reportid>\d*)\}")
78def report_id_from_text(text):
79    m = billing_report_regex.match(text)
80    if m:
81        return int(m.groupdict()["reportid"])
82
83def get_billing_reports(comp):
[7000]84    billing_reports = set()
85    rows = dbhelper.get_all(comp, "SELECT id FROM custom_report")[1]
86    if rows:
87        billing_reports = set([x[0] for x in rows])
88    return billing_reports
[4280]89
[7011]90
[4260]91class RowFilter(object):
92    """A genshi filter that operates on table rows, completely hiding any that
93    are in the billing_reports table."""
94
95    def __init__(self, comp):
96        self.component = comp
[4280]97        self.billing_reports = get_billing_reports(comp)
[4260]98        self.component.log.debug('self.billing_reports= %r' % self.billing_reports)
99
100    def __call__(self, row_stream):
[7011]101        #stream = Stream(list(row_stream))
102        def tryInt(v):
103            try:
104                return int(v)
105            except:
106                return None
107        streams = split_stream(row_stream)
108        #report_urls = [tryInt(i.get('href').split('/')[-1]) for i in stream.select('td[@class="report"]/a/@href')]
109        #self.component.log.debug("ReportRowFilter: #%s#,  %r" % (len(streams), list(report_urls)))
110        for stream in streams:
111            show_row = True
112            try:
113                report_url = stream.select('td[@class="report"]/a/@href').render()
114                id = tryInt(report_url.split('/')[-1])
115                self.component.log.debug("Report row filter: about to filter: %s not in %s : %s" % (id, self.billing_reports,  not id in self.billing_reports) )
116                show_row = not id in self.billing_reports
117            except Exception, e:
118                self.component.log.exception("Report row filter failed")
119                show_row = True #Dont Hide Error Rows?
120            if show_row:
121                for kind,data,pos in stream:
[6826]122                    yield kind,data,pos
[4260]123
124class ReportsFilter(Component):
125    """Remove all billing reports from the reports list."""
126    implements(ITemplateStreamFilter)
127
128    def filter_stream(self, req, method, filename, stream, data):
[7011]129        if not filename in ('report_view.html', 'report_list.html'):
[4260]130            return stream
131        self.log.debug("Applying Reports Filter to remove T&E reports")
132        return stream | Transformer(
133            '//table[@class="listing reports"]/tbody/tr'
[6790]134            ).apply(FilterTransformation(RowFilter(self)))
[4280]135
136class ReportScreenFilter(Component):
[4293]137    """Hides TandE reports even when you just go to the url"""
[4280]138    implements(ITemplateStreamFilter)
139    def __init__(self):
140        self.billing_reports = get_billing_reports(self)
141        self.log.debug('ReportScreenFilter: self.billing_reports= %r' % self.billing_reports)
142
143    def filter_stream(self, req, method, filename, stream, data):
[7011]144        if not filename in ('report_view.html', 'report_list.html'):
[4280]145            return stream
146        reportid = [None]
147
148        def idhelper(strm):
149            header = strm[0][1]
150            if not reportid[0]:
151                self.log.debug("ReportScreenFilter: helper: %s %s %s"%(strm,header,report_id_from_text(header)))
[6790]152                reportid[0] = report_id_from_text(header)
[4280]153            for kind, data, pos in strm:
154                yield kind, data, pos       
155               
156        def permhelper(strm):
157            id = reportid[0]
158            self.log.debug("ReportScreenFilter: id:%s, in bill: %s   has perm:%s" % (id, id in self.billing_reports, req.perm.has_permission("TIME_VIEW")))
159            if id and id in self.billing_reports and not req.perm.has_permission("TIME_VIEW"):
160                self.log.debug("ReportScreenFilter: No time view, prevent render")
161                msg = "YOU MUST HAVE TIME_VIEW PERMSSIONS TO VIEW THIS REPORT"
162                for kind, data, pos in tag.span(msg).generate():
163                    yield kind, data, pos
164            else:
165                for kind, data, pos in strm:
166                    yield kind, data, pos
167
[6790]168        self.log.debug("ReportScreenFilter: About to begin filtering of billing reports without permissions")
169        stream = stream | Transformer('//div[@id="content"]/h1/text()').apply(FilterTransformation(idhelper))
170        stream = stream | Transformer('//div[@id="content"]').apply(FilterTransformation(permhelper))
[4280]171        return stream
[4281]172
173## ENFORCE PERMISSIONS ON report exports
174
175billing_report_fname_regex = re.compile("report_(?P<reportid>\d*)")
176def report_id_from_filename(text):
[5494]177    if text:
178        m = billing_report_fname_regex.match(text)
179        if m:
180            return int(m.groupdict()["reportid"])
181    return -1;
[4281]182
183
184def _send_csv(self, req, cols, rows, sep=',', mimetype='text/plain',
185              filename=None):
186    req.send_response(200)
187    req.send_header('Content-Type', mimetype + ';charset=utf-8')
188    if filename:
189        req.send_header('Content-Disposition', 'filename=' + filename)
190    req.end_headers()
191   
192    id = report_id_from_filename(filename)
193    reports = get_billing_reports(self)
[4282]194    if id in reports and not req.perm.has_permission("TIME_VIEW"):
[4281]195        raise RequestDone
196   
197    def iso_time(t):
198        return format_time(t, 'iso8601')
199
200    def iso_datetime(dt):
201        return format_datetime(dt, 'iso8601')
202
203    col_conversions = {
204        'time': iso_time,
205        'datetime': iso_datetime,
206        'changetime': iso_datetime,
207        'date': iso_datetime,
208        'created': iso_datetime,
209        'modified': iso_datetime,
210        }
211   
212    converters = [col_conversions.get(c.strip('_'), unicode) for c in cols]
213   
214    writer = csv.writer(req, delimiter=sep)
215    writer.writerow([unicode(c).encode('utf-8') for c in cols])
216    for row in rows:
217        row = list(row)
218        for i in xrange(len(row)):
219            row[i] = converters[i](row[i]).encode('utf-8')
220        writer.writerow(row)
221
222    raise RequestDone
223
224ReportModule._send_csv = _send_csv
Note: See TracBrowser for help on using the repository browser.