Edgewall Software

Ticket #2669: report-by-xls-for-trac0_9_5.diff

File report-by-xls-for-trac0_9_5.diff, 7.1 KB (added by anonymous, 3 years ago)

Report to Native-xls by pyExcelerator for trac 0.9.5

  • ticket/report.py

     
    2828from trac.wiki import wiki_to_html, IWikiSyntaxProvider 
    2929 
    3030 
     31try: 
     32    from pyExcelerator import * 
     33    class XlsDoc(CompoundDoc.XlsDoc): 
     34        def get(self, stream): 
     35            padding = '\x00' * (0x1000 - (len(stream) % 0x1000)) 
     36            self.book_stream_len = len(stream) + len(padding) 
     37            self.__build_directory() 
     38            self.__build_sat() 
     39            self.__build_header() 
     40            return '%s%s%s%s%s%s%s' % ( 
     41                self.header, 
     42                self.packed_MSAT_1st, 
     43                stream, 
     44                padding, 
     45                self.packed_MSAT_2nd, 
     46                self.packed_SAT, 
     47                self.dir_stream) 
     48 
     49    class Workbook(Workbook): 
     50        def get(self): 
     51           doc = XlsDoc() 
     52           return doc.get(self.get_biff_data()) 
     53 
     54    has_pyexcel = 1 
     55except: 
     56    has_pyexcel = 0 
     57 
    3158class ColumnSorter: 
    3259 
    33     def __init__(self, columnIndex, asc=1): 
     60    def __init__(self, columnIndex, asc=1, enums=None): 
    3461        self.columnIndex = columnIndex 
    3562        self.asc = asc 
     63        self.enums = enums 
    3664 
    3765    def sort(self, x, y): 
    3866        const = -1 
     
    4270        # make sure to ignore case in comparisons 
    4371        realX = x[self.columnIndex] 
    4472        if isinstance(realX, (str, unicode)): 
    45             realX = realX.lower() 
     73            if self.enums and self.enums.has_key(realX): 
     74                realX = self.enums[realX] 
     75            else: 
     76                realX = realX.lower() 
    4677        realY = y[self.columnIndex] 
    4778        if isinstance(realY, (str, unicode)): 
    48             realY = realY.lower() 
     79            if self.enums and self.enums.has_key(realY): 
     80                realY = self.enums[realY] 
     81            else: 
     82                realY = realY.lower() 
    4983 
    5084        result = 0 
    5185        if realX < realY: 
     
    305339            if colIndex != None: 
    306340                k = 'report.headers.%d.asc' % (colIndex - hiddenCols) 
    307341                asc = req.args.get('asc', None) 
     342                enums = None 
     343                from trac.ticket import model 
     344                clses = {'severity':model.Severity, 'priority':model.Priority, 
     345                         'resolution':model.Resolution, 
     346                         'type':model.Type, 'status':model.Status} 
     347                if clses.has_key(sortCol): 
     348                    scls = clses[sortCol] 
     349                    enums = dict([(enum.name, enum.value) for enum in scls.select(self.env, db=db)]) 
    308350                if asc: 
    309                     sorter = ColumnSorter(colIndex, int(asc)) 
     351                    sorter = ColumnSorter(colIndex, int(asc), enums=enums) 
    310352                    req.hdf[k] = asc 
    311353                else: 
    312                     sorter = ColumnSorter(colIndex) 
     354                    sorter = ColumnSorter(colIndex, enums=enums) 
    313355                    req.hdf[k] = 1 
    314356                rows.sort(sorter.sort) 
    315357 
     
    372414        elif format == 'tab': 
    373415            self._render_csv(req, cols, rows, '\t') 
    374416            return None 
     417        elif format == 'xls': 
     418            self._render_xls(req, cols, rows) 
     419            return None 
    375420 
    376421        return 'report.cs', None 
    377422 
     
    390435                 'Comma-delimited Text', 'text/plain') 
    391436        add_link(req, 'alternate', '?format=tab' + href, 
    392437                 'Tab-delimited Text', 'text/plain') 
     438        if has_pyexcel: 
     439            if self.env.config.getbool('ticket', 'show_excel_link'): 
     440                add_link(req, 'alternate', '?format=xls' + href, 
     441                         'Excel', 'application/vnd.ms-excel') 
    393442        if req.perm.has_permission('REPORT_SQL_VIEW'): 
    394443            add_link(req, 'alternate', '?format=sql', 'SQL Query', 
    395444                     'text/plain') 
     
    485534            req.write('-- %s\n\n' % '\n-- '.join(description.splitlines())) 
    486535        req.write(sql) 
    487536         
     537    def _render_xls(self, req, cols, rows): 
     538        req.send_response(200) 
     539        req.send_header('Content-Type', 'application/vnd.ms-excel') 
     540        req.send_header('Content-Disposition', 
     541                        'filename=Report%s.xls' % req.hdf['report.id']) 
     542        req.end_headers() 
     543 
     544        wb = Workbook() 
     545        sheetname = "%s - %s" % (req.hdf['report.title'], 
     546                                 req.hdf['project.name']) 
     547        ws = wb.add_sheet(sheetname.decode('utf-8')) 
     548        ws.panes_frozen = True 
     549        ro = 1 
     550        ws.horz_split_pos = ro 
     551 
     552        import copy 
     553 
     554        font0 = Font() 
     555        font0.charset = font0.CHARSET_SYS_DEFAULT 
     556        font0.name = 'MS UI Gothic' 
     557        font1 = copy.copy(font0) 
     558        font1.bold = True 
     559        font2 = copy.copy(font0) 
     560        font2.height = 0x00A0 
     561        align0 = Alignment() 
     562        align1 = copy.copy(align0) 
     563        align1.vert = align1.VERT_TOP 
     564        align2 = copy.copy(align1) 
     565        align2.wrap = align2.WRAP_AT_RIGHT 
     566 
     567        style0 = XFStyle() 
     568        style0.font = font0 
     569        style0.alignment = align1 
     570        style0.num_format_str = 'general' 
     571        style_colheader = copy.copy(style0) 
     572        style_colheader.num_format_str = '@' 
     573        style_colheader.font = font1 
     574        style_num = copy.copy(style0) 
     575        style_str = copy.copy(style0) 
     576        style_str.num_format_str = '@' 
     577        style_wrap_str = copy.copy(style0) 
     578        style_wrap_str.alignment = align2 
     579        style_wrap_str.font = font2 
     580        style_date = copy.copy(style0) 
     581        style_date.num_format_str = 'yyyy/mm/dd' 
     582 
     583        for col, cx in map(lambda x, y: [x, y], cols, range(len(cols))): 
     584            name = str(col[0]).replace('_','') 
     585            ws.write(ro-1, cx, name.decode('utf-8'), style_colheader) 
     586            conv = lambda x: str(x).replace('\r', '') \ 
     587                                   .rstrip('\r\n') \ 
     588                                   .decode('utf-8') 
     589            style = style_str 
     590            if name in ['time', 'date','changetime', 'created', 'modified']: 
     591                ws.col(cx).width = 0xb00 
     592                from datetime import datetime 
     593                conv = lambda x: datetime.fromtimestamp(float(x)) 
     594                style = style_date 
     595            elif name in ['summary', 'description']: 
     596                if name == 'description': 
     597                    ws.col(cx).width = 0x7000 
     598                else: 
     599                    ws.col(cx).width = 0x1a00 
     600                style = style_wrap_str 
     601            elif name in ['color', 'ticket', 'id']: 
     602                if name in ['color']: 
     603                    ws.col(cx).hidden = 1 
     604                conv = lambda x: int(x) 
     605                style = style_num 
     606            elif name in ['style']: 
     607                ws.col(cx).hidden = 1 
     608            for value, rx in map(lambda x, y: [conv(x[cx]), ro + y], \ 
     609                                 rows, range(len(rows))): 
     610                ws.write(rx, cx, value, style) 
     611        req.write(wb.get()) 
     612 
    488613    # IWikiSyntaxProvider methods 
    489614     
    490615    def get_link_resolvers(self):