From d7d440cb2f24066ac0a09e4fa0e5922ef2d6e4c4 Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Mon, 6 Jan 2014 23:22:39 +0100 Subject: [PATCH] style & documentation refresh, xls footer datetime fix --- report_xls/__init__.py | 2 +- report_xls/__openerp__.py | 58 ++++++++++++++++---- report_xls/report_xls.py | 83 +++++++++++++++-------------- report_xls/static/src/img/icon.png | Bin 0 -> 11645 bytes report_xls/utils.py | 11 ++-- 5 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 report_xls/static/src/img/icon.png diff --git a/report_xls/__init__.py b/report_xls/__init__.py index aab5a45b..835cd44c 100644 --- a/report_xls/__init__.py +++ b/report_xls/__init__.py @@ -12,7 +12,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index c8fea849..5e85adac 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -12,7 +12,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License @@ -20,24 +20,60 @@ # ############################################################################## { - 'name': 'XLS report engine', - 'version': '0.3', + 'name': 'Excel report engine', + 'version': '0.4', 'license': 'AGPL-3', 'author': 'Noviat', 'website': 'http://www.noviat.com', 'category': 'Reporting', - 'description': """ + 'description': """ +Excel report engine +=================== + +This module adds Excel export capabilities to the standard OpenERP reporting engine. + +Report development +'''''''''''''''''' +In order to create an Excel report you can\n +- define a report of type 'xls' +- pass ``{'xls_export': 1}`` via the context to the report create method + +The ``report_xls`` class contains a number of attributes and methods to facilitate +the creation XLS reports in OpenERP. + +* cell types + + Supported cell types : text, number, boolean, date. + +* cell styles + + The predefined cell style definitions result in a consistent + look and feel of the OpenERP Excel reports. + +* cell formulas + + Cell formulas can be easily added with the help of the ``rowcol_to_cell()`` function which + you can import from the ``utils.py`` module. + +* Excel templates + + It is possible to define Excel templates which can be adapted by 'inherited' modules. + Download the ``account_move_line_report_xls`` module from http://apps.openerp.com + as example. + +* XLS with multiple sheets + + Download the ``account_journal_report_xls`` module from http://apps.openerp.com as example. + +Development assistance +'''''''''''''''''''''' +Contact info@noviat.com for help with the development of Excel reports in OpenERP, . -This module adds XLS export capabilities to the standard OpenERP reporting engine. - -In order to generate an XLS export you can define a report of type 'xls' or alternatively pass {'xls_export' : 1) via the context to create method of the report. - """, 'depends': ['base'], 'external_dependencies': {'python': ['xlwt']}, - 'demo_xml': [], - 'init_xml': [], - 'update_xml' : [], + 'demo': [], + 'data': [], 'active': False, 'installable': True, } diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index e8263a2f..5e19c859 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -2,8 +2,8 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# -#Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. +# +# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -12,7 +12,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License @@ -23,7 +23,8 @@ import xlwt from xlwt.Style import default_style import cStringIO -import datetime, time +from datetime import datetime +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT import inspect from types import CodeType from openerp.report.report_sxw import * @@ -32,13 +33,15 @@ from openerp.tools.translate import translate, _ import logging _logger = logging.getLogger(__name__) + class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self + class report_xls(report_sxw): - + xls_types = { 'bool': xlwt.Row.set_cell_boolean, 'date': xlwt.Row.set_cell_date, @@ -53,36 +56,29 @@ class report_xls(report_sxw): } # TO DO: move parameters infra to configurable data - + # header/footer - DT_FORMAT = '%Y-%m-%d %H:%M:%S' + DT_FORMAT = '%Y-%m-%d %H:%M:%S' hf_params = { 'font_size': 8, - 'font_style': 'I', # B: Bold, I: Italic, U: Underline - } - xls_headers = { - 'standard': '' + 'font_style': 'I', # B: Bold, I: Italic, U: Underline } - xls_footers = { - 'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DT_FORMAT) + - '&R&%(font_size)s&%(font_style)s&P / &N') %hf_params - } - + # styles - _pfc = '26' # default pattern fore_color - _bc = '22' # borders color + _pfc = '26' # default pattern fore_color + _bc = '22' # borders color decimal_format = '#,##0.00' - date_format = 'YYYY-MM-DD' + date_format = 'YYYY-MM-DD' xls_styles = { 'xls_title': 'font: bold true, height 240;', 'bold': 'font: bold true;', 'underline': 'font: underline true;', 'italic': 'font: italic true;', - 'fill': 'pattern: pattern solid, fore_color %s;' %_pfc, - 'fill_blue' : 'pattern: pattern solid, fore_color 27;', - 'fill_grey' : 'pattern: pattern solid, fore_color 22;', - 'borders_all': 'borders: left thin, right thin, top thin, bottom thin, ' \ - 'left_colour %s, right_colour %s, top_colour %s, bottom_colour %s;' %(_bc,_bc,_bc,_bc), + 'fill': 'pattern: pattern solid, fore_color %s;' % _pfc, + 'fill_blue': 'pattern: pattern solid, fore_color 27;', + 'fill_grey': 'pattern: pattern solid, fore_color 22;', + 'borders_all': 'borders: left thin, right thin, top thin, bottom thin, ' + 'left_colour %s, right_colour %s, top_colour %s, bottom_colour %s;' % (_bc, _bc, _bc, _bc), 'left': 'align: horz left;', 'center': 'align: horz center;', 'right': 'align: horz right;', @@ -91,8 +87,8 @@ class report_xls(report_sxw): 'bottom': 'align: vert bottom;', } # TO DO: move parameters supra to configurable data - - def create(self, cr, uid, ids, data, context=None): + + def create(self, cr, uid, ids, data, context=None): self.pool = pooler.get_pool(cr.dbname) self.cr = cr self.uid = uid @@ -105,11 +101,13 @@ class report_xls(report_sxw): if report_xml.report_type == 'xls': return self.create_source_xls(cr, uid, ids, data, context) elif context.get('xls_export'): + self.table = data.get('model') or self.table # use model from 'data' when no ir.actions.report.xml entry return self.create_source_xls(cr, uid, ids, data, context) return super(report_xls, self).create(cr, uid, ids, data, context) def create_source_xls(self, cr, uid, ids, data, context=None): - if not context: context = {} + if not context: + context = {} parser_instance = self.parser(cr, uid, self.name2, context) self.parser_instance = parser_instance objs = self.getObjects(cr, uid, ids, context) @@ -119,15 +117,22 @@ class report_xls(report_sxw): wb = xlwt.Workbook(encoding='utf-8') _p = AttrDict(parser_instance.localcontext) _xs = self.xls_styles + self.xls_headers = { + 'standard': '', + } + self.xls_footers = { + 'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT) + + '&R&%(font_size)s&%(font_style)s&P / &N') % self.hf_params, + } self.generate_xls_report(_p, _xs, data, objs, wb) wb.save(n) n.seek(0) - return (n.read(), 'xls') - + return (n.read(), 'xls') + def render(self, wanted, col_specs, rowtype, render_space='empty'): """ - returns 'mako'-rendered col_specs - + returns 'evaluated' col_specs + Input: - wanted: element from the wanted_list - col_specs : cf. specs[1:] documented in xls_row_template method @@ -139,7 +144,7 @@ class report_xls(report_sxw): caller_space = inspect.currentframe().f_back.f_back.f_locals localcontext = self.parser_instance.localcontext render_space.update(caller_space) - render_space.update(localcontext) + render_space.update(localcontext) row = col_specs[wanted][rowtype][:] for i in range(len(row)): if isinstance(row[i], CodeType): @@ -155,9 +160,9 @@ class report_xls(report_sxw): def xls_row_template(self, specs, wanted_list): """ Returns a row template. - + Input : - - 'wanted_list': list of Columns that will be returned in the row_template + - 'wanted_list': list of Columns that will be returned in the row_template - 'specs': list with Column Characteristics 0: Column Name (from wanted_list) 1: Column Colspan @@ -190,14 +195,14 @@ class report_xls(report_sxw): if s_len > 7 and s[7] is not None: c.append(s[7]) else: - c.append(None) + c.append(None) r.append((col, c[1], c)) col += c[1] break if not found: _logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w) return r - + def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False): r = ws.row(row_pos) for col, size, spec in row_data: @@ -209,9 +214,9 @@ class report_xls(report_sxw): data = report_xls.xls_types_default[spec[3]] if size != 1: if formula: - ws.write_merge(row_pos, row_pos, col, col+size-1, data, style) + ws.write_merge(row_pos, row_pos, col, col + size - 1, data, style) else: - ws.write_merge(row_pos, row_pos, col, col+size-1, data, style) + ws.write_merge(row_pos, row_pos, col, col + size - 1, data, style) else: if formula: ws.write(row_pos, col, formula, style) @@ -220,5 +225,5 @@ class report_xls(report_sxw): if set_column_size: ws.col(col).width = spec[2] * 256 return row_pos + 1 - + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/static/src/img/icon.png b/report_xls/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c GIT binary patch literal 11645 zcmV-@ErQaCP)C<(Ewy&_zRo_QU6!mF+ma>OGPaS8u~8CiBn}u1hBz_CkYEBFAYd@d z0mlSR90-sTAF{B8gA-#m8?dqLVA+yoOV&tZ&C+{UbyZh&SJkVx-h1=?_r9v>nVtm6 zI^j5PdaA3cU%hwlfB*mczkj*Kwbtywf%hb5$~+h52S>`7rof)% z%dPWI)W3MH^<*Pn@5QY&>u|>L5ve3yi!-ept+>6oQc5%Y(?T<$v^|ujyK(xfvu{Y; zo++g=moYEQfoDp&+PpRc&M)F*dWzs>FZaSzSu z6^+nmA?HOUTZmYK8kLeK29eIg=cWAedH{rEDew znc|G$%i-Lm%AeywN;3jYZ|+|trR`%nx)i;yuqsTZtftydg_ZMbJlEseg5WIWW~gS7 zND4+GT=8f}BzpTG3qF9eS@Gq&KfW966MdE&l9)nm2F#8i24%$q-WNoc_E z;@pnkA-&>eyhd7f=(vyO`=+hUd<19v-lQL8f8*fXUf)QYhmMdR`P`rX^51^Y$U!pU zeD#7bhr^U(N2oc>K@XVo^wv{YTI$`KN-&@%^MaRKk@8&HeiaAR?$RW zfFL}&g`zuTLd7iD>M#G*Qy>4Evma`PXQxWD_h0|rcV2h@4Rg27x-(|19NL?Z!E>z= zodxzPN(~h~*Q~4b?wZPbO??)ZRc7>r%PX&@mGa8vDdlOFapqX9QS7-L?*HwN4nbzX zI*+b>^249|(3e|(I#Xb8dC`x*@|JHrF>_0qO=1ZKX`JaSPNO&rrOqT*Lb}DYG@Q=< zwY4~~1&5OXjH zlQ@pzC`n_-DZrK`0hO_HG7dasqrpD!LqB(@H?TUohjF8d!)+BAE08{@(r`x4K@ifo zN#i46RTjlb7xvQi-}&@!{Nbhd&z4XB^Oyh7eJ5X4U=x{4yS?rp49$EQcYur8)$7Eb zyJ5b-LysOtLNvZZUj`jIirYmwEP9I8zf{*yKFa6OU zTzKzv>BZlF&(FVf`jtFzTHShG?Q%`5(VFn+7V>LJBeY@S=9yix=OKxaAYdFiW&KB2 zKlR@p`&+i{<;hn&nl*y0wRk(#Q7Py3+y*&0OqeG&2D(!G*IJ5$<9hB3cFj5Q z1E)x(_aW%>@>#AV6&k>*2F&~TlYjBd?w_2QdD|P$JXB(H&3^iCDPEk4GQ=3nI?8_glIo?0M^T7#!YLJ9$n``iw08q^d2@rqdK8_H1!0V6z zGN}-X3G?P~A{a_~;6{ocaHY&3D2WO-YT#kZ5Hna;pJnOmYg)0!uGJi*> zA3yv1yXoqKH~r*o3wL1F8;zA9>^UxC2c?bryUM9nYBc#Qh1bk73GHyXtE9|iWnQVkq%a+*R1WpPLWC0M)=4oy;7W26@>kJE>T%-ttB zb@{x~5fFsyhphkDrH}P8_Vp*e{lwI*Q1wP*L+T_;O;*V=49Wa*!E=kWx`IM{2xJ&h z<~fBSYBG#dGMYXKjHaS85{K0xk{Dh#GkLfVn@t}R-_2!t-6^15Q#E^lB8F; z{useU$sjYr9BP;mtbk4`t@J+qF%bsw33Uya`uj|SU1&aiVerf=r@!@v*_$xbt#%{D zWwK1T9#n!{68wN<1+QQ*K-=^oSv92}OzsHauQN>wZpQ_y0z-kLpo{?Y)Q;aQF4UHA z2NN_{$vuR*4pDXPnB~=y+!47UQW9>YX+~j;(QrpeQp%Ko5vFNk083_Z9A|M#2(D$V zT)q47VOQ9IRql&H4?<`DjvKO#r&~{KF?Pqo%V+(u-e6~KubyJC5O0>+*aKq{T2#s< z>m+eAtcU~qAX7EzL#c)1YLR6uCF_tbDZ4a^P56@HY62fKs6e`dz{bIcY%-E^Z8=rf zCh*aEL=f}k#^s?~&?^WFr#r?Rgw9oykIuw9sm{W0y6+8dxch$J^HaGigiEP5bI>B} zLu0|kFgeZLMt|ed=7l+zU3cu1V9w57bFbI&d};GjoIDp+Mpp_|&-bk|VA&ysBuUn` zR&*(JJ=b$Q7>?_>aIS*9d*)uYAbE~Qae1-JH1Bq~W;w9a!JdMf)Z9xsnmK=<0%pEliArRnbjH55GP+?`j_!MQ)_)N%ahnS zL^}uACHZzcz~ zSn1z>C)0+x1BbGr^iY)p`Cs&=*K-WUMY-I5P5_pc(q#M>`Kat zzh};g>02Ls{dfJ`@4szkVIDW~Ws$2=mcYe_(#xK#f8x(S{?YG#^HE3p0=MBRmSMZ! zxU~6nYj?B0x7CAR%%GeA7ROq!q!ndpD8p{t*lezEtbXru(=Xddi2Jk#mQ+VXa% znZTtBazX{a0JFsv;Jiz$=$n^NFNfYD6Y!Kvhv#vECsTQ2tlRB=>QjGv;>5|LM~=_W zFS?y{2SL|#d75#r7xm*L0QM1dk#i%MfibvQp;nsI`_S1UI??Uax$E@Tz5dJJ_sFGR z_bW4rQpOX2?Zld=^W#f@^vPek>H6F6J@Za$G}pJk>9yZ<>c~k0&RUQY;azh`!*oeZfd~<+jrn63NH+^q7N(lG zwK8EO;}KS262C<|IE3^UUkzqzw3M6eRAoP zN{l+gRl)ekg$;IsW__^P3U;90gmbRTb7kd#5@LY_L$v;28?AP$zES_i``-i!z_tK5 z0>a`B@s2Dj6bkSi6iHFZP{L*TI3Z19kT%5%l+YWD8LQF5Gn@sx26O^2&}$KM3CdUl7j7o=7(^ej22Nr zRw|Z(R$fc|Y@xUwiedUsZ5inaG0Ulg{uVvFADkf7GdQ z>%^vcUX-L?aAa^RhH@DCO}<6<G{Qv|F8eK zymh`G?@E>!7fy5;mW1RPJyzun-8RyWy`SD+U!)xAH5>;0h;D(4=6Y3~izr%2% zB*KrRl#qhH8F8BU!c3fEHg24YL&%CJNfM10YaYcuHrydv0YUWpJwOS(Ay8D9god4llg&(r+CT6Ym@pK)j3r35F z7u{KIS)s{T;G?cnUfZ}dJ2$^@cmdi%0htuN-LM5mvV8ts-~1DAe&E4caY7|2^whRf z7(obagAp58!@~^*aT=$T9FlC9kI=*Z0f%gcn;Zs70iOrOo|>2f-5928%%F7E>-CIF zgC>WuNS(0kts`N#<>X%JSelWbJ1v87_8b>P@CXc=I+kULq zV=TpNe3?+BNSaD|26pM4ed^0WuMhSEm1%ExS1xXJo8d3~;BS1}gKw=B#~Ecn@gVKY z4U?e`8ZY(SjH;6{i?+Lst=85~YbOb^6f7i!9V@^ZB`I*DOxc_wHhn3 zSj2?3(g)qaRR=-j`z7BiLmQ0iQ8FSyNg`Za4S3?=JcKMln)U)-t&JDGf{Ccbm8p(A z1;@0nY#2(RfjDyP=zVv*=`)XhbiBGG*)WAjhD)b+yR8e)tew2!WTz9HKY!uI>C^9i z_q%Vq;dV#5lv&_;?+3dgVar`fjzc?y7|Y8mPo8}&NP;vIWxrDJON9dBOu)ZOyusmk z#6O$(0tRPMQZAMN{RqBXuy)X1?f}O6{Xwx%hWmtFko(+c@!F(|C>??+VTJJ)c-Eju<=f8O`q-0yH;4uxc3S6cQYH_j993R9e~HI_ zKkD6o^FR9Dw|?*9+!9X>K`>z$H@#K+)sKc@s@sAhQ~wS3yz$IUFX?nUje4uy*{yGH zwRd-RI<582N-J6cov!**1+QH4N}gK)$jYuR7t6v1@TRB<5v6Iox3>o=04zOXoZKBn zBQ^m^nMXyb5k)uyhnh{)CV}2LeX?-F`7k=f=mBjKyf{d^tMv<8t+i%%E0WQzx1D+9 zbDt=(s+|ylJ_&=Mk7weqKKuD!{{Hv9^;_OHS(=4+;7Sx9bp7vNNa@t2EHOC_|$L#DI%H z1eTkVY3N)NO$JTtBuO%O0mF7pLO?(x3^QTE<(Ff)9Gz6EBWT@T&{}Pt-|DP3+8eur z_8V6)o!e{mbZ3ScY+3-KqTWtW^U2H>DHUq zH&(JNAyTA;DGHEUz%d^lAN$w8{tKsH^wRt9dtDqwzFV-Pzjh5`)Lmuxkg%CtGDtOA z6J;|HChcQS_n13Tnc!8%rsu8M3zOV3@=$RS_WS+0xmjq0O^%6CJC-#KlfJhZMq&}L zqH4Jc1Iu-4SURz3bAWWCy|S|P%vNi;zO&lu)_d_D0&uGEWZCTe?CrO_Y!F58^=V9A z1Be4<&RC{A38Q)62i~{VSQQ08K&FRPGMh3mG}-(xJA7`n0`Wistwm76W9>jg=!Y70 z91v6zwV_><q>LNo&WMl*>YLiyS-Y_N#hq@e-QDRYy+p<&%|s4|FK>R)$>j@|wwsNj zUnQ1GjYX~r8ME0!@sWog|DE6c(7WFCP7r)Ycn-B6LGdWJ3kZhg`%Zz!lpE5n$P6Ag zI`y^Y8a%4&J2kfkRU#r_etg6el+fUgq68A$nDSVP@L0iFaB|2*Nla;g#7W6s7=;B( zgAsZC*ep3Y<3x&7Ne4g;;GC+>`l2W@fEA%ARd*aLaMBIh%bORs+G|_U@?L*8j6%w* zb2Usr5*UD6Vw^92*=>LFr<++Exx&*n%r=o0xXbi)h4=l!`>Qjh(`W90HF}Qmfz*wg zW}wnhq#||Ob+^xq&k;{CJh~I?E^n{&(rzpxFNHL)qVRv6Yi`#MefkRkVE-f77ijzm|`tNu2v&P_O4Bi-PU8UwpamJ$=vjuFkPXLCP(PN zlT^j)t(EiZUuo?%I=!84*h;u2?f%m-UJ*BY{JRJZa&f;CT#>V!!XTS9JH@*8Mr6StPDb_X@@ck#={FpCi%jXpZm2)820V zz*~R(HTS;8R|QY|){{U|y|9O9ayRZ!Ag^PymXxnEfdZvuaeJx;VnH+y!)SS9dHU3} zFn+;G!Aw+y14{E5e(hSYBza`EJ%X}c4&YQe8I>0}fVVI;IZ1UI2*@E{3hS5{o4)tV zeT|*QR(+$>*-OH7uh(w3T6=>|qC(@Guqc$_hryD5x*#^C>yO{~*+(AnfVzgpnLH~Y zB+#Nz<)8V~7Z9P(9-8b0-JOkgx~G5YT|ayGOYikEpL!CAP!Z_CR6)ZskpV2hwH2{@ z6MjgOA2-B~4|@|Ji@2ZE+x60|W$Q=Ggzc^2yaU1$6Lej6pM2<~jLc}o5IycRnr=K% zDOR|lQV;_`xa5!BbmBILB446jWJ((0U<8av(=>>J_HMh^?f1I9y?+w}$-t>?B=#SoU%NaNE2y+M{m9b5- znn66E4o{wDJabiEF=7_OO{`9|SQw{)I69VDsH){LmjVeJgHYiX=X85rh+u@2`*;NP zN`h}?!ts0kV6(n;Zm0FonYWZn6*FRO9JnJ>igO`cK$>SLMap%lG(K6e2@jGNK{G&? zrEsnocS->>>&Lx!T>7U!^uzDDu(?_(diy$jFlyl`-rWg5{WqU@=z+JrG^ zue;^=?HQ07WIE^ajsW@8o7G^5}V!u4&4re6vZRlAfu2mjl(Qs+BU7 z!?StS=`av@7DX6cGdR%N`5 zfSPFHum0M<+uE%Au5b4x7yvhk7Ak(iG@>wzQi5}g#j%-oSS*%4BaTaHpp2X6HmRj# zL5OnMAGHEn2Z_)V7*R03^Rh;mlo_*?E1IA;ED*YEX>k$G-_lr`y&66rQD2iJy_yk| zjTX!=;~UH;@u5WWvEsygf8wX#{+-`bZ#J6Eo$g-u!ubmqFI;Txw4QkMNqqFNzxu*& z{`=qlu^;_cgtne%a_t6IRg4h;djLNDC<^10N{GWoXEkg%uz6r0}RrG+Jnam3gt8JPC_y=JQgwpVY|S65cHo7+c^9v$vcq6e&7 z#*+GG{oJ$9hOiN;;_-r`;r-}lRK=mB77U6}2xokfWS~Q3w^S+=M(u`niJ=jL!NBuN zqlk-*n$q{jJ5=eG>51txl%X@tB4#vdAO}002D}nv;SqBvxuWENcbf7F8x>dqCY0=> zH8V*@W};6LmhB1Qwp3|kO?Mwz{*-NfdDtT3+`R0J8m8ce6 zAXw6%;|1R-y0njk^4x}E*v(7Y+M4-4bRX1b!feMB8+HTbMQ;;*>r~%$$6YTueL9I# z1pm98-Mz5e3;NsJ%{H8N2W)?Rd#Bl?48-QDu2+N?^wKxq@<5?jvARhpr?le=koSHN z99k@tN~Oyh!G>*(c@n^M;`UOuG`mo63XGZub7}5H?QYcUhpj=>F*5Tw_dVxw4~DI~ zacZ4V?fUMSo9}wbZFf?W2xEg}(2sYi-fe1<of9A!F^ ze6SCMJO$8Vw+QQ=D@|t(oV2n%7ilJwEJ@RB(C@ZEVFvx3PG|Yz^7weI;1`E&6?Ov{ zPY@JDF_v${ri^K1M9fS_lX0XTr4E2@#}|$in2+ZP*mdr7pr8#H@yXD@66ENmTx zIlj(&y|}G7V&#K=u>8#WOK!Z|GwmAO$vVOI*|oEg3^S2>RQ4|v-9klF%5E70H5s## zFyR1V1eb^fb)s1v)kCduMbR;-_=G|LDp?Ji$-t|fvR9-|t?I<7#bZ?9VM@MQ=2;5K zY&pZ4XOd)D%4~*ipGFL`9wVTjdkA*u&|yItPi3lt)bKqB1I(WUFNwCH)y8TDHpp@81-p&itw9>ZI)ZNl|Mj?EfI=~flxz!+=M@RZ{X(%&5(t6A z^oA2R&Q8yo#w;pBVg7)5nwvow+jnEza17v>ZCyl;DbluBZNX?bJx%R^TEq8X&1^Da zgp61A3Y+sYmEBe?O;1cyG)Q@m%+wIoAnZx1R7a*hN@!$=uXU$JEXs7o!4-t5&x)9$ zaU&#%Ubxp9G=iuX$}p8FJP5SiGy+ifB*l)dC#WaS!TGppuhm;QxAp^X|Ce)9bFQX- zvv#k2arGjY0^%y4TwkF|$#_CX+=#44kXgckXj|R1R0r;IpI0Pv)C^u2H@5EcfFSm5 zOH4I8K6}SRb;_YY84*}Yai>ngq@U>o#F;o8sjOqTw_+o=Nm4Nx;8;$NEtbn=N_dfK zf@=q<4nsK@WPv8XM$L1^|EghQq_9#+=-fdt?F2X{y@Qo43hqG>ryxEMp=wc zdXDcaPK8S^A!KX4ve<18`OVxo7hbHXq@TPC0@y z5yHp#(qs^AKD+Vl-}u(kx15&Qusz+Z6G+6c#^qjipSESwfJ)vD)0gbn;S*)QDk3MZ zvQtW@A0>e{IY84-Ya+fYtV$0LgnjA6+>}q#pc%uO22qe#6_icCa#Nu+vQuoRQwDRT z(20tJc&>Jc0N;QeK%qD2?S}1E*bHEsfI6bU>Co#tl#-xpxdl<;9_7~io!;v5`EL;q zAx^!l^ax=xx!3KMh-Vc>>Og{!rlZu9fM_l4UdltW6lG|agM@K%ax{*ey-qjk1xW~1 zh5R8}hcLvk8>R`HO^*P6Vik{%Pf*BUj5WmaBoQ-_G6kyF(7y`L~U`%s?F5#@NeHUR8)(2A#UPT*TT&^rOI=qMOR83I$N^n$(4>AezPsB) z2yPRWmj%Ix-HW7Ng7EKDiOtT;annGY?_%d-dUE!jn_s=Qwzj?9*xhZdw!Q*81iNL0 z0=R9lR01~ughTBkfNCe1sLf!`@FI>6wThA~A~{iA*|P<6EYpe z$@JtTHO-RRh(u;k$xMxUO!IE;D=I<5Z&fh8SPUV|WOWj*c>if;k41urt5_)PM@nY) zYY5ABp={K}n6Z;%XC`aY;QLfcg*TNM*8kR4fBr`44`pcF1iRRI$hnIfKzjk!s+QGDWP z7v`8o@#OfF;N}WcB}x16J1W!mGqzLtU^yuNDhc`zzB683gdq0G$izER4gxCwUM&b^ znjvUc0kubDEN0g)-dHMBT<%%&59I^+V0k@BMWLc!881vNPEmi!ncH6m6^)W8Ns}my z>dkr>4H}K+)<$!2W{DI0Pzii{WBchZKh>23u#sw|A}IGxx&yzDD9IRv5+=Vu-a5j^ z1n{X^samQ8HWj8(O08aihm>}^Ftcqy$)PLAqihn7%7ec&ORCi|@;m(US67%ez^vEr zS1V)tQ`p8d%y%Og>yng;&)_?c7ThB78UiA7S27VArdGqqo;1kp`_xXES4xHQM48Xe zERbGPharO#qb*RJQ2N-BQ*VCFcWyQ|+wIQs`o&JKwHd5K0jV_$KC)Ey3*g)c?x?5_ zHB5V{O4xX<#)g^LIE|nYh>0l)PJ(buMom}-k_RT2OM^CwRn=;hAJCc*D;7l-Aog!s zb@_1HNuG}xz;_;nLpGf~%IcF*@)4-p?^DRXRe{5GF8>i`! z!zYg%KEWw8S9`r~LM8cbv$fr9Zf|aE?)G+Mdb0ZBMcYJ_hWO1n!5zG(U}wo@m;**iUK90DWyiD zoS!J;^doajU2lAc$}l&0geZ=ht!BH^K|sB7X*KBfC(08{+FlX9)!zBiSDsd;;kpRY zNQFJB*N_Se6V5x_?gi1b3xZNtgDe}bjZ+7i=}Nc4fC_{m{vvJO;0lc>M(#vnvaQN{ z!<}z@?U~n>C)`eFuer6;?FIVis^z2F7DX^gyY0vZ&c!#$dVS{|w0h*I1HQdiPjDzysZOn>Qn8Wr2+buVFBfVm$Pu=q3lQ*Q4 zbR7WdfHjC9H(Q%qjYgxjeSYoPRMf7jdNd*_7NHp2SI?f zvf0Wj1ko%t+>-yBpZN8OPtG{fQB;1`!LD4tRDX8u{7$=<)!C=N%-9ZNQU009W9;~$ zn~xkhy0o}7H@`49KX>TRp_!Q(fKwInDObv#?~-9rv}bqe?b`uGe1Ejj&(h7j5x~?% zOpKWKC=4@tyr>IWgJ#Ow#Q4G_(?>{#2z&qpAQwQtUeK%8>u~CgMt!5+z|TxPFEvwvVKOXfm79rO7Ij(dFHY+k$;r5P$Zrr-zMOdPK+}kU^4kcLzz3#ZgL)&x2kN5Q*yb`h;y^hyXDR zL+XMhj}*c`BfM>Gwd_-8 z6pIC$M|NFj^u1bC5M+`M_c4yBk{*7)iuK?5*8$k9j8u7QhS(N?LJiPXtWvX*%3Q(K z&_qcPL166y#}!`V*wjt?c-a+hSIrxvml_Ta5oDUi@W7ZnCXB~k52AsHdL$5#&B374 z>BQzMSD=1ixaKr`Dp^_CID7USu{C1@02gS=!aOu)c4~SG0>VqMh6)U;2I+X#CkQGW zs64g)LsG%k>B&h<<-42XDWu-F=yeEBA_- zy)TrMbh%$BGWz0{d}*ZJm}xfsVyba$rXR<+M_>+mLXrv1JM<=A#R-4tP z8<47Lj^ogZ>R5GdjwG_MFgI)d&CJe@RY`GbwJ}5tRKCE_9n;MRQ26w%?~K5R3% zOhV15!9GcIOJug82xoyttDqM`(&cj5*5yaCaR1LcA+LSlgQlSF&==Y;HZd{$o;cbq zPSqM5Ns>h77z__+P%u39_V(zlU;w*<1dS-Ek$tDt+1lKG=huCMPD-gt0BY#1H;G93- z^YhJD2c%=ZTYz*2f*{Db(y|~WOuzv1z{<=1HkK9U(pg#m%DHFLEVQP8WuKg!nwy)S znwkRi%+Jp+EiDnjO?$dJ_QG+(#heQBM&@gBO;%~SNcL)$8y!=`b#NT(Ass|-S5T&ug z6{=&}Lo3;EyKxc$I_JD%G zzW_?DMqK{-q^t54|34nqPhYjd2M_x_9FPTV9=@`uf#ZJznpD3AGd6+600000NkvXX Hu0mjfi6p;w literal 0 HcmV?d00001 diff --git a/report_xls/utils.py b/report_xls/utils.py index 30ce800d..e4f3d878 100644 --- a/report_xls/utils.py +++ b/report_xls/utils.py @@ -12,17 +12,18 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## -# + def _render(code): - return compile(code, '', 'eval') + return compile(code, '', 'eval') + def rowcol_to_cell(row, col, row_abs=False, col_abs=False): # Code based upon utils from xlwt distribution @@ -41,9 +42,9 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False): else: col_abs = '' if d > 0: - chr1 = chr(ord('A') + d - 1) + chr1 = chr(ord('A') + d - 1) chr2 = chr(ord('A') + m) # Zero index to 1-index return col_abs + chr1 + chr2 + row_abs + str(row + 1) - + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: