/[papo]/papo/ruff/ruff.py
ViewVC logotype

Diff of /papo/ruff/ruff.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.2 by fheinz, Sat Nov 22 00:12:33 2003 UTC revision 1.3 by jlenton, Mon Nov 24 13:54:39 2003 UTC
# Line 1  Line 1 
1  # Copyright 2003 Fundacion Via Libre  # -*- coding: latin1 -*-
2    #
3    # Copyright 2003 Fundación Via Libre
4  #  #
5  # This file is part of PAPO.  # This file is part of PAPO.
6  #  #
7  # PAPO is free software; you can redistribute it and/or modify  # PAPO is free software; you can redistribute it and/or modify
8  # it under the terms of the GNU General Public License as published by  # it under the terms of the GNU General Public License as published by
9  # the Free Software Foundation; either version 2 of the License, or  # the Free Software Foundation; either version 2 of the License, or
10  # (at your option) any later version.  # (at your option) any later version.
11  #  #
12  # PAPO is distributed in the hope that it will be useful,  # PAPO is distributed in the hope that it will be useful,
13  # but WITHOUT ANY WARRANTY; without even the implied warranty of  # but WITHOUT ANY WARRANTY; without even the implied warranty of
14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  # GNU General Public License for more details.  # GNU General Public License for more details.
16  #  #
17  # You should have received a copy of the GNU General Public License  # You should have received a copy of the GNU General Public License
18  # along with PAPO; if not, write to the Free Software  # along with PAPO; if not, write to the Free Software
19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20    
21  from __future__ import nested_scopes  from UserList import UserList
22  import sys  from xml.dom.ext.reader import PyExpat
23  import urllib  from xml.dom import Node
24  import xml.sax  from xml import xpath
25  from xml.sax.handler import ContentHandler  
26    import pathetic
27  from Layouts import *  import compute
28  from Stuff import *  
29    from errors import *
30  class Handler(ContentHandler):  
31      meth_dict = { "header": "block",  def init_dentry_from_xml(dentry, dom):
32                    "page_header": "block",      for box in xpath.Evaluate('box', dom):
33                    "partial_header": "block",          p=[]
34                    "footer": "block",          for i in box.childNodes:
35                    "page_footer": "block",              if i.nodeType == Node.TEXT_NODE:
36                    "partial_footer": "block",                  p.append(i.data)
37                    "detail_entry": "dentry",              elif i.nodeName == 'field':
38                    }                  name = i.getAttribute('name')
39      class_dict = { "header": Block,                  default = i.getAttribute('default')
40                     "page_header": Block,                  p.append(pathetic.Field(dentry, name, default))
41                     "partial_header": Block,              elif i.nodeName == 'compute':
42                     "footer": Block,                  expr = i.getAttribute('expr')
43                     "page_footer": Block,                  watch = i.getAttribute('watch')
44                     "partial_footer": Block,                  if watch:
45                     "detail_entry": Dentry,                      watch = [j.strip() for j in watch.split(',')]
46                     "box": Box,                  else:
47                     "detail": Detail,                      watch = []
48                     "compute": Compute,                  p.append(compute.Compute(dentry, expr, watch))
49                     "field": Field,          kw={}
50                    }          for i in ('line', 'column', 'width', 'height'):
51                kw[i] = int(box.getAttribute(i) or 0)
52      def __init__(self, template):          for i in ('vfill', 'raw', 'wrap'):
53          ContentHandler.__init__(self)              kw[i] = box.getAttribute(i) == 'true'
54          self.stack = [template]          kw['align'] = {"right": 1, "center": 0}.setdefault(box.getAttribute('align'), -1)
55            dentry.add_box(*p, **kw)
56      def characters(self, chars):  
57          #chars = chars.strip()  def init_detail_from_xml(detail, dom):
58          if chars.strip():      for den in xpath.Evaluate('detail_entry', dom):
59              self.stack[-1].append(Literal(chars))          d = detail.add_dentry(den.getAttribute('name'))
60            init_dentry_from_xml(d, den)
61      def startElement(self, name, attrs):      for det in xpath.Evaluate('detail', dom):
62          method = getattr(self, "start_" + self.meth_dict.get(name, name), None)          d = detail.add_detail(det.getAttribute('name'))
63          if callable(method):          init_detail_from_xml(d, det)
64              self.stack.append(method(name, attrs))      for i in ('header', 'footer'):
65            for j in ('', 'page_', 'partial_'):
66                n=j+i
67                d = xpath.Evaluate(n, dom)
68                if d:
69                    d=d[0]
70                    try:
71                        h = int(d.getAttribute('height'))
72                    except ValueError:
73                        raise InvalidExtremaError, \
74                              "%s/%s should have an explicit, numeric, height attribute" \
75                              % (detail.fqdn, n)
76                    x=pathetic.Shelf(detail, n, h)
77                    setattr(detail, n, x)
78                    init_dentry_from_xml(x, d)
79                else:
80                    setattr(detail, n, None)
81    
82    class Page:
83        def __init__(self, height, max_height, prev):
84            if height:
85                self.expand = 1
86          else:          else:
87              try:              self.expand = 0
88                  c = self.class_dict[name]          self.target_height = max_height
89              except KeyError:          if prev is None:
90                  raise DocumentLayoutError, "I don't know how to handle a %s" % name              self.details = []
91                            self.data = []
92              thing = c(attrs)          else:
93              thing._type = name              self.details = prev.details[:]
94              thing._parent = self.stack[-1]              # el encabezado de una página es cada uno de los page_headers de
95              self.stack[-1].append(thing)              # los detalles abiertos desde el de más afuera hasta más adentro,
96              self.stack.append(thing)              # seguido de todos los partial_header en el mismo orden
97                self.data = filter(None, [i.page_header for i in self.details]
98      def endElement(self, name):                                 + [i.partial_header or i.header for i in self.details])
99          method = getattr(self, "end_" + self.meth_dict.get(name, name), None)          self.prev = prev
         if callable(method):  
             method()  
         self.stack.pop()  
   
     def start_block(self, name, attrs):  
         block = Block(attrs)  
         detail = self.stack[-1]  
         block.name = name  
           
         detail.append(block)  
         detail.by_name[name] = block  
100                    
         return block  
101    
102      def start_dentry(self, name, attrs):      def post(self):
103          dentry = Dentry(attrs)          # el pie de una página es el partial_footer de cada uno de los
104          detail = self.stack[-1]          # detalles abiertos, seguido de los page_footer de cada uno de los
105          detail.append(dentry)          # detalles abiertos, de adentro hacia afuera.
106          detail.by_name[dentry.name] = dentry          post = filter(None,
107                          [i.page_footer for i in self.details]
108          return dentry                        + [i.partial_footer for i in self.details])
109            post.reverse()
110      def start_field(self, name, attrs):          return post
         field = Field(attrs)  
         box = self.stack[-1]  
         block = self.stack[-2]  
111                    
         box.append(field)  
         block.by_name[field.name] = field  
           
         return field  
   
     def start_detail(self, name, attrs):  
         detail = Detail(attrs)  
         parent = self.stack[-1]  
         parent.append(detail)  
         parent.by_name[detail.name] = detail  
112    
113          return detail      def extend(self, other):
114            self.data.extend(other)
115    
116        def append(self, other):
117            self.data.append(other)
118    
119  class Template(Detail):      def data_length(self):
120      def __init__(self, url=None):          l=0
121          Detail.__init__(self, {"name": "Template"})          for i in self.data:
122          if url is not None:              if isinstance(i, pathetic.Pathetic):
123              self.parse(url)                  l+=i.height
124                else:
125                    l+=1
126            return l
127    
128        def __len__(self):
129            if not self.details:
130                return 0
131            h1 = self.data_length()
132            h2 = reduce(lambda a, b: a+b.height, self.post(), 0)
133            if self.details[-1].footer:
134                h2 = max(h2, self.details[-1].footer.height)
135            return h1+h2
136            return h1
137    
138        def replace(self, old, new):
139            # linear search -- sucks, pero qué se le va a hacer
140            i = None
141            page = self
142            while page:
143                try:
144                    i = page.data.index(old)
145                    page.data = page.data[:i] + new + page.data[i+1:]
146                    page = page.prev
147                except ValueError:
148                    page = page.prev
149                
150        def add_detail(self, detail):
151            self.details.append(detail)
152            if detail.header:
153                self.append(detail.header)
154            
155    
156      def parse(self, url):      def close_detail(self):
157          try:          d = self.details.pop()
158              self.file = urllib.urlopen(url)          for i in ('header', 'footer'):
159              xml.sax.parse(self.file, Handler(self))              for j in ('', 'page_', 'partial_'):
160          finally:                  n=j+i
161              self.file.close()                  x = getattr(d, n, None)
162                    if x:
163                        self.replace(x, x.render())
164    
165        def render(self):
166            post = self.post()
167            r=[]
168            for i in self.data:
169                if isinstance(i, pathetic.Pathetic):
170                    r.extend(i.render())
171                else:
172                    r.append(i)
173            maxrlen = self.target_height - len(post)
174            if self.target_height and len(r) < maxrlen and self.expand:
175                i=None
176                if self.details:
177                    i = self.details[0].dict.get(self.details[-1].fqdn + '_empty/')
178                if i:
179                    i = i.render()
180                else:
181                    i = [' '*len(r[-1])]
182                r.extend(i*(maxrlen - len(r) - 1))
183            for i in post:
184                if isinstance(i, pathetic.Pathetic):
185                    r.extend(i.render())
186                else:
187                    r.append(i)
188            r.append('\f')
189            return r
190    
191    
192    class Report:
193        def __init__(self, file):
194            (dom,) = xpath.Evaluate('/report', PyExpat.Reader().fromUri(file))
195            max_height = int(dom.getAttribute('max_height') or 0)
196            height = int(dom.getAttribute('height') or 0)
197            max_num_pages = int(dom.getAttribute('max_num_pages') or 0)
198            template = pathetic.Template(max_num_pages=max_num_pages,
199                                         max_height=max_height,
200                                         height=height)
201            init_detail_from_xml(template, dom)
202            self.template = template
203            self.init_stack()
204            self.init_pages()
205            self.path = []
206            self.add_detail('')
207            self.set_extrema()
208    
209        def set_extrema(self):
210            for i in ('header', 'footer'):
211                for j in ('', 'page_', 'partial_'):
212                    n=j+i
213                    if self.details:
214                        x = getattr(self.details[-1], n)
215                    else:
216                        x = None
217                    setattr(self, n, x)
218    
219        def init_stack(self):
220            self.details = []
221    
222        def init_pages(self):
223            self.pages = []
224            self.add_page()
225    
226        def add_page(self):
227            prev = None
228            if self.pages:
229                prev = self.pages[-1]
230            page = Page(self.template.height, self.template.max_height, prev)
231            self.pages.append(page)
232    
233        def guess_fqdn(self, name):
234            if self.path:
235                return '/'.join(self.path) + '/' + name + '/'
236            else:
237                return '/'
238    
239        def add_dentry(self, name, **kw):
240            d = self.template.dict[self.guess_fqdn(name)]
241            out = d.render(**kw)
242            if self.template.max_height and \
243                   len(self.pages[-1]) + len(out) > self.template.max_height:
244                if self.template.max_num_pages and \
245                       len(self.pages) >= self.template.max_num_pages:
246                    raise ReportOverflowError, "no more space in report"
247                else:
248                    self.add_page()
249            self.pages[-1].extend(out)
250    
251        def add_detail(self, name):
252            detail = self.template.dict[self.guess_fqdn(name)]
253            self.path.append(name)
254            self.details.append(detail)
255            self.pages[-1].add_detail(detail)
256            self.set_extrema()
257    
258        def close_detail(self):
259            self.pages[-1].close_detail()
260            self.details.pop()
261            self.path.pop()
262            self.set_extrema()
263    
264        def render(self):
265            render = []
266            pageno = 1
267            compute.set_numpages(len(self.pages))
268            for i in self.pages:
269                compute.set_pageno(pageno)
270                pageno += 1
271                render.extend(i.render())
272            return render
273    
274        def __str__(self):
275    
276            # la impresión ocurre al completar. Entonces lo que hay que hacer es
277            # cerrar todos los detalles que queden abiertos (menos el último, para
278            # que salga después el page_footer)
279            while len(self.details) > 1:
280                self.close_detail()
281                
282            s = "\n".join(["\n".join(self.render())])
283            return s
284    
285        if __name__=='__main__':
286        import time
287        r=Report('test.xml')
288        r.add_detail('bar')
289        #r.header(fecha=time.ctime())
290        r.add_dentry('item', moneda='Morlacks with cochinillas arábigas', contado=100, ctacte=850, otros=50, total=1000)
291        r.add_dentry('item', moneda='MORLACKS WITH COCHINILLAS ARÁBIGAS', contado=10, ctacte=85, otros=5, total=100)
292        r.add_dentry('item', moneda='Morlacks with cochinillas arábigas', contado=100, ctacte=850, otros=50, total=1000)
293        r.add_dentry('item', moneda='Morlacks with cochinillas arábigas', contado=100, ctacte=850, otros=50, total=1000)
294        r.add_detail('foo')
295        r.add_dentry('test')
296        r.add_dentry('test')
297        r.add_dentry('test')
298        r.close_detail()
299        r.add_detail('foo')
300        r.add_dentry('test')
301        r.add_dentry('test')
302        r.add_dentry('test')
303        r.close_detail()
304        r.add_dentry('item', moneda='Morlacks with cochinillas arábigas', contado=100, ctacte=850, otros=50, total=1000)
305        print r
306    

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26