~vauxoo/addons-vauxoo/7.0-ifrs_agrinos-dev-hbto

« back to all changes in this revision

Viewing changes to commission_payment/report/tiny_sxw2rml/tiny_sxw2rml.py

  • Committer: Miguel Delgado
  • Date: 2012-05-21 14:48:48 UTC
  • Revision ID: miguel.delgado07@gmail.com-20120521144848-v9lt237qbqh2cwdl

[ADD] se migraron los modulos de baremo, commission_payment y hr_salesman_commission
para que el proceso de calculo de comisiones funcionara igual que en version 5.0 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# -*- encoding: utf-8 -*-
 
3
##############################################################################
 
4
#
 
5
# Copyright (c):
 
6
#
 
7
#     2005 pyopenoffice.py Martin Simon (http://www.bezirksreiter.de)
 
8
#     2005 Fabien Pinckaers, TINY SPRL. (http://tiny.be)
 
9
#
 
10
# WARNING: This program as such is intended to be used by professional
 
11
# programmers who take the whole responsability of assessing all potential
 
12
# consequences resulting from its eventual inadequacies and bugs
 
13
# End users who are looking for a ready-to-use solution with commercial
 
14
# garantees and support are strongly adviced to contact a Free Software
 
15
# Service Company
 
16
#
 
17
# This program is Free Software; you can redistribute it and/or
 
18
# modify it under the terms of the GNU General Public License
 
19
# as published by the Free Software Foundation; either version 2
 
20
# of the License, or (at your option) any later version.
 
21
#
 
22
# This program is distributed in the hope that it will be useful,
 
23
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
24
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
25
# GNU General Public License for more details.
 
26
#
 
27
# You should have received a copy of the GNU General Public License
 
28
# along with this program; if not, write to the Free Software
 
29
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
30
#
 
31
##############################################################################
 
32
 
 
33
"""
 
34
Tiny SXW2RML - The Open ERP's report engine
 
35
 
 
36
Tiny SXW2RMLis part of the Tiny report project.
 
37
Tiny Report is a module that allows you to render high quality PDF document
 
38
from an OpenOffice template (.sxw) and any relationnal database.
 
39
 
 
40
The whole source code is distributed under the terms of the
 
41
GNU Public Licence.
 
42
 
 
43
(c) 2005 pyopenoffice.py Martin Simon (http://www.bezirksreiter.de)
 
44
(c) 2005-TODAY, Fabien Pinckaers - Tiny sprl
 
45
"""
 
46
__version__ = '0.9'
 
47
 
 
48
 
 
49
import re
 
50
import string
 
51
import os
 
52
import zipfile
 
53
import xml.dom.minidom
 
54
from reportlab.lib.units import toLength
 
55
import base64
 
56
 
 
57
class DomApiGeneral:
 
58
    """General DOM API utilities."""
 
59
    def __init__(self,content_string="",file=""):
 
60
        self.content_string = content_string
 
61
        self.re_digits = re.compile(r"(.*?\d)(pt|cm|mm|inch|in)")
 
62
 
 
63
    def _unitTuple(self,string):
 
64
        """Split values and units to a tuple."""
 
65
        temp = self.re_digits.findall(string)
 
66
        if not temp:
 
67
            return (string,"")
 
68
        else:
 
69
            return (temp[0])
 
70
 
 
71
    def stringPercentToFloat(self,string):
 
72
        temp = string.replace("""%""","")
 
73
        return float(temp)/100
 
74
 
 
75
    def findChildrenByName(self,parent,name,attr_dict={}):
 
76
        """Helper functions. Does not work recursively.
 
77
        Optional: also test for certain attribute/value pairs."""
 
78
        children = []
 
79
        for c in parent.childNodes:
 
80
            if c.nodeType == c.ELEMENT_NODE and c.nodeName == name:
 
81
                children.append(c)
 
82
        if attr_dict == {}:
 
83
            return children
 
84
        else:
 
85
            return self._selectForAttributes(nodelist=children,attr_dict=attr_dict)
 
86
 
 
87
    def _selectForAttributes(self,nodelist,attr_dict):
 
88
        "Helper function."""
 
89
        selected_nodes = []
 
90
        for n in nodelist:
 
91
            check = 1
 
92
            for a in attr_dict.keys():
 
93
                if n.getAttribute(a) != attr_dict[a]:
 
94
                    # at least one incorrect attribute value?
 
95
                    check = 0
 
96
            if check:
 
97
                selected_nodes.append(n)
 
98
        return selected_nodes
 
99
 
 
100
    def _stringToTuple(self,s):
 
101
        """Helper function."""
 
102
        try:
 
103
            temp = string.split(s,",")
 
104
            return int(temp[0]),int(temp[1])
 
105
        except:
 
106
            return None
 
107
 
 
108
    def _tupleToString(self,t):
 
109
        try:
 
110
            return self.openOfficeStringUtf8("%s,%s" % (t[0],t[1]))
 
111
        except:
 
112
            return None
 
113
 
 
114
    def _lengthToFloat(self,value):
 
115
        v = value
 
116
        if not self.re_digits.search(v):
 
117
            return v
 
118
        try:
 
119
            if v[-4:] == "inch":
 
120
                # OO files use "inch" instead of "in" in Reportlab units
 
121
                v = v[:-2]
 
122
        except:
 
123
            pass
 
124
        try:
 
125
            c = round(toLength(v))
 
126
            return c
 
127
        except:
 
128
            return v
 
129
 
 
130
    def openOfficeStringUtf8(self,string):
 
131
        if type(string) == unicode:
 
132
            return string.encode("utf-8")
 
133
        tempstring = unicode(string,"cp1252").encode("utf-8")
 
134
        return tempstring
 
135
 
 
136
class DomApi(DomApiGeneral):
 
137
    """This class provides a DOM-API for XML-Files from an SXW-Archive."""
 
138
    def __init__(self,xml_content,xml_styles):
 
139
        DomApiGeneral.__init__(self)
 
140
        self.content_dom = xml.dom.minidom.parseString(xml_content)
 
141
        self.styles_dom = xml.dom.minidom.parseString(xml_styles)
 
142
        body = self.content_dom.getElementsByTagName("office:body")
 
143
        self.body = body and body[0]
 
144
 
 
145
        # TODO:
 
146
        self.style_dict = {}
 
147
        self.style_properties_dict = {}
 
148
 
 
149
        # ******** always use the following order:
 
150
        self.buildStyleDict()
 
151
        self.buildStylePropertiesDict()
 
152
        if self.styles_dom.getElementsByTagName("style:page-master").__len__()<>0:
 
153
            self.page_master = self.styles_dom.getElementsByTagName("style:page-master")[0]
 
154
        if  self.styles_dom.getElementsByTagName("style:page-layout").__len__()<>0 :
 
155
                        self.page_master = self.styles_dom.getElementsByTagName("style:page-layout")[0]        
 
156
        self.document = self.content_dom.getElementsByTagName("office:document-content")[0]
 
157
 
 
158
    def buildStylePropertiesDict(self):
 
159
        for s in self.style_dict.keys():
 
160
            self.style_properties_dict[s] = self.getStylePropertiesDict(s)
 
161
 
 
162
    def updateWithPercents(self,dict,updatedict):
 
163
        """Sometimes you find values like "115%" in the style hierarchy."""
 
164
        if not updatedict:
 
165
            # no style hierarchies for this style? =>
 
166
            return
 
167
        new_updatedict = copy.copy(updatedict)
 
168
        for u in new_updatedict.keys():
 
169
            try:
 
170
                if new_updatedict[u].find("""%""") != -1 and dict.has_key(u):
 
171
                    number = float(self.re_digits.search(dict[u]).group(1))
 
172
                    unit = self.re_digits.search(dict[u]).group(2)
 
173
                    new_number = self.stringPercentToFloat(new_updatedict[u]) * number
 
174
                    if unit == "pt":
 
175
                        new_number = int(new_number)
 
176
                        # no floats allowed for "pt"
 
177
                        # OOo just takes the int, does not round (try it out!)
 
178
                    new_updatedict[u] = "%s%s" % (new_number,unit)
 
179
                else:
 
180
                    dict[u] = new_updatedict[u]
 
181
            except:
 
182
                dict[u] = new_updatedict[u]
 
183
        dict.update(new_updatedict)
 
184
 
 
185
    def normalizeStyleProperties(self):
 
186
        """Transfer all style:style-properties attributes from the
 
187
        self.style_properties_hierarchical dict to the automatic-styles
 
188
        from content.xml. Use this function to preprocess content.xml for
 
189
        XSLT transformations etc.Do not try to implement this function
 
190
        with XSlT - believe me, it's a terrible task..."""
 
191
        styles_styles = self.styles_dom.getElementsByTagName("style:style")
 
192
        automatic_styles = self.content_dom.getElementsByTagName("office:automatic-styles")[0]
 
193
        for s in styles_styles:
 
194
            automatic_styles.appendChild(s.cloneNode(deep=1))
 
195
        content_styles = self.content_dom.getElementsByTagName("style:style")
 
196
        # these are the content_styles with styles_styles added!!!
 
197
        for s in content_styles:
 
198
            c = self.findChildrenByName(s,"style:properties")
 
199
            if c == []:
 
200
                # some derived automatic styles do not have "style:properties":
 
201
                temp = self.content_dom.createElement("style:properties")
 
202
                s.appendChild(temp)
 
203
                c = self.findChildrenByName(s,"style:properties")
 
204
            c = c[0]
 
205
            dict = self.style_properties_dict[(s.getAttribute("style:name")).encode("latin-1")] or {}
 
206
            for attribute in dict.keys():
 
207
                c.setAttribute(self.openOfficeStringUtf8(attribute),self.openOfficeStringUtf8(dict[attribute]))
 
208
 
 
209
    def transferStylesXml(self):
 
210
        """Transfer certain sub-trees from styles.xml to the normalized content.xml
 
211
        (see above). It is not necessary to do this - for example - with paragraph styles.
 
212
        the "normalized" style properties contain all information needed for
 
213
        further processing."""
 
214
        # TODO: What about table styles etc.?
 
215
        outline_styles = self.styles_dom.getElementsByTagName("text:outline-style")
 
216
        t = self.content_dom.createElement("transferredfromstylesxml")
 
217
        self.document.insertBefore(t,self.body)
 
218
        t_new = self.body.previousSibling
 
219
        try:
 
220
            page_master = self.page_master
 
221
            t_new.appendChild(page_master.cloneNode(deep=1))
 
222
            t_new.appendChild(outline_styles[0].cloneNode(deep=1))
 
223
        except:
 
224
            pass
 
225
 
 
226
    def normalizeLength(self):
 
227
        """Normalize all lengthes to floats (i.e: 1 inch = 72).
 
228
        Always use this after "normalizeContent" and "transferStyles"!"""
 
229
        # TODO: The complex attributes of table cell styles are not transferred yet.
 
230
        #all_styles = self.content_dom.getElementsByTagName("style:properties")
 
231
        #all_styles += self.content_dom.getElementsByTagName("draw:image")
 
232
        all_styles = self.content_dom.getElementsByTagName("*")
 
233
        for s in all_styles:
 
234
            for x in s._attrs.keys():
 
235
                v = s.getAttribute(x)
 
236
                s.setAttribute(x,"%s" % self._lengthToFloat(v))
 
237
                # convert float to string first!
 
238
 
 
239
    def normalizeTableColumns(self):
 
240
        """Handle this strange table:number-columns-repeated attribute."""
 
241
        columns = self.content_dom.getElementsByTagName("table:table-column")
 
242
        for c in columns:
 
243
            if c.hasAttribute("table:number-columns-repeated"):
 
244
                number = int(c.getAttribute("table:number-columns-repeated"))
 
245
                c.removeAttribute("table:number-columns-repeated")
 
246
                for i in range(number-1):
 
247
                    (c.parentNode).insertBefore(c.cloneNode(deep=1),c)
 
248
 
 
249
    def buildStyleDict(self):
 
250
        """Store all style:style-nodes from content.xml and styles.xml in self.style_dict.
 
251
        Caution: in this dict the nodes from two dom apis are merged!"""
 
252
        for st in (self.styles_dom,self.content_dom):
 
253
            for s in st.getElementsByTagName("style:style"):
 
254
                name = s.getAttribute("style:name").encode("latin-1")
 
255
                self.style_dict[name] = s
 
256
        return True
 
257
 
 
258
    def toxml(self):
 
259
        return self.content_dom.toxml(encoding="utf-8")
 
260
 
 
261
    def getStylePropertiesDict(self,style_name):
 
262
        res = {}
 
263
 
 
264
        if self.style_dict[style_name].hasAttribute("style:parent-style-name"):
 
265
            parent = self.style_dict[style_name].getAttribute("style:parent-style-name").encode("latin-1")
 
266
            res = self.getStylePropertiesDict(parent)
 
267
 
 
268
        childs = self.style_dict[style_name].childNodes
 
269
        for c in childs:
 
270
            if c.nodeType == c.ELEMENT_NODE and c.nodeName.find("properties")>0 :
 
271
                for attr in c._attrs.keys():
 
272
                    res[attr] = c.getAttribute(attr).encode("latin-1")
 
273
        return res
 
274
 
 
275
class PyOpenOffice(object):
 
276
    """This is the main class which provides all functionality."""
 
277
    def __init__(self, path='.', save_pict=False):
 
278
        self.path = path
 
279
        self.save_pict = save_pict
 
280
        self.images = {}
 
281
 
 
282
    def oo_read(self,fname):
 
283
        z = zipfile.ZipFile(fname,"r")
 
284
        content = z.read('content.xml')
 
285
        style = z.read('styles.xml')
 
286
        all = z.namelist()
 
287
        for a in all:
 
288
            if a[:9]=='Pictures/' and len(a)>10:
 
289
                pic_content = z.read(a)
 
290
                self.images[a[9:]] = pic_content
 
291
                if self.save_pict:
 
292
                    f=open(os.path.join(self.path, os.path.basename(a)),"wb")
 
293
                    f.write(pic_content)
 
294
                    f.close()
 
295
        z.close()
 
296
        return content,style
 
297
 
 
298
    def oo_replace(self,content):
 
299
        regex = [
 
300
            (r"<para[^>]*/>", ""),
 
301
            #(r"<text:ordered-list.*?>(.*?)</text:ordered-list>", "$1"),
 
302
            #(r"<text:unordered-list.*?>(.*?)</text:unordered-list>", "$1"),
 
303
            (r"<para(.*)>(.*?)<text:line-break[^>]*/>", "<para$1>$2</para><para$1>"),
 
304
        ]
 
305
        for key,val in regex:
 
306
            content = re.sub(key, val, content)
 
307
        return content
 
308
 
 
309
    def unpackNormalize(self,sourcefile):
 
310
        c,s = self.oo_read(sourcefile)
 
311
        c = self.oo_replace(c)
 
312
        dom = DomApi(c,s)
 
313
        dom.normalizeStyleProperties()
 
314
        dom.transferStylesXml()
 
315
        dom.normalizeLength()
 
316
        dom.normalizeTableColumns()
 
317
        new_c = dom.toxml()
 
318
        return new_c
 
319
 
 
320
def sxw2rml(sxw_file, xsl, output='.', save_pict=False):
 
321
    import libxslt
 
322
    import libxml2
 
323
    tool = PyOpenOffice(output, save_pict = save_pict)
 
324
    res = tool.unpackNormalize(sxw_file)
 
325
    styledoc = libxml2.parseDoc(xsl)
 
326
    style = libxslt.parseStylesheetDoc(styledoc)
 
327
    doc = libxml2.parseMemory(res,len(res))
 
328
    result = style.applyStylesheet(doc, None)
 
329
 
 
330
    root = result.xpathEval("/document/stylesheet")
 
331
    if root:
 
332
        root=root[0]
 
333
        images = libxml2.newNode("images")
 
334
        for img in tool.images:
 
335
            node = libxml2.newNode('image')
 
336
            node.setProp('name', img)
 
337
            node.setContent( base64.encodestring(tool.images[img]))
 
338
            images.addChild(node)
 
339
        root.addNextSibling(images)
 
340
    try:
 
341
        xml = style.saveResultToString(result)
 
342
        return xml
 
343
    except:
 
344
        return result
 
345
 
 
346
if __name__ == "__main__":
 
347
    import optparse
 
348
    parser = optparse.OptionParser(
 
349
        version="Tiny Report v%s" % __version__,
 
350
        usage = 'tiny_sxw2rml.py [options] file.sxw')
 
351
    parser.add_option("-v", "--verbose", default=False, dest="verbose", help="enable basic debugging")
 
352
    parser.add_option("-o", "--output", dest="output", default='.', help="directory of image output")
 
353
    (opt, args) = parser.parse_args()
 
354
    if len(args) != 1:
 
355
        parser.error("incorrect number of arguments")
 
356
 
 
357
    import sys
 
358
    import StringIO
 
359
 
 
360
    fname = sys.argv[1]
 
361
    f = StringIO.StringIO(file(fname).read())
 
362
    xsl_file = 'normalized_oo2rml.xsl'
 
363
    z = zipfile.ZipFile(fname,"r")
 
364
    mimetype = z.read('mimetype')
 
365
    if mimetype.split('/')[-1] == 'vnd.oasis.opendocument.text' :
 
366
                xsl_file = 'normalized_odt2rml.xsl'
 
367
    xsl = file(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), xsl_file)).read()
 
368
    result = sxw2rml(f, xsl, output=opt.output, save_pict=False)
 
369
 
 
370
    print result
 
371
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 
372