[render] pycompta-render to html, json or csv (closes #268913)

authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
changeset21f227c3477f
branchdefault
phasedraft
hiddenno
parent revision#770f22a7a535 rapports configurables (closes #257021)
child revision#aa7d88c58d0a [render_*] ajoute write_historique_compte à render_mod()
files modified by this revision
bin/pycompta-render
lib/render_csv.py
lib/render_html.py
lib/render_json.py
main.py
# HG changeset patch
# User Nicolas Chauvat <nicolas.chauvat@logilab.fr>
# Date 1414008850 -7200
# Wed Oct 22 22:14:10 2014 +0200
# Node ID 21f227c3477fcfe0ef118193e85fc95f752bbd18
# Parent 770f22a7a535d109a07465e6f7994336a682168e
[render] pycompta-render to html, json or csv (closes #268913)

diff --git a/bin/pycompta-render b/bin/pycompta-render
@@ -1,44 +1,36 @@
1  #!/usr/bin/python
2 
3  import sys
4  import os.path as osp
5 -import glob
6 -import codecs
7 -import xml.etree.ElementTree as ET
8 -from pycompta.lib import render_html
9 -
10 -target = sys.argv[1]
11 -if not target.endswith('/'): target += '/'
12 -basedir = osp.dirname(target)
13 -planc = {}
14 -for compte in ET.parse(osp.join(target, 'plan-comptable.xml')).findall('.//compte'):
15 -    planc[compte.get('numero')] = compte.get('nom')
16 -cr_precedent = ET.parse(osp.join(target, 'compte-resultat_precedent.xml'))
17 -bilan_precedent = ET.parse(osp.join(target, 'bilan_precedent.xml'))
18 +import optparse
19 
20 -for path in glob.glob(osp.join(basedir,'*.xml')):
21 -    basename = osp.basename(path)
22 -    if basename.startswith('journal'):
23 -        dest = path.replace('journal', 'journal2').replace('.xml', '.html')
24 -        print 'rendering journal', dest
25 -        render_html.write_journal(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
26 -    elif basename.startswith('grand-livre'):
27 -        dest = path.replace('grand-livre', 'balance2').replace('.xml', '.html')
28 -        print 'rendering balance', dest, 'from', path
29 -        render_html.write_balance(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
30 -        dest = path.replace('grand-livre', 'grand-livre2').replace('.xml', '.html')
31 -        print 'rendering grand-livre', dest, 'from', path
32 -        render_html.write_grandlivre(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
33 -        dest = path.replace('grand-livre', 'tva2').replace('.xml', '.html')
34 -        print 'rendering tva', dest, 'from', path
35 -        render_html.write_tva(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
36 -    elif basename.startswith('compte-resultat'):
37 -        dest = path.replace('compte-resultat', 'compte-resultat2').replace('.xml', '.html')
38 -        print 'rendering compte-resultat', dest, 'from', path
39 -        render_html.write_compte_resultat(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, cr_precedent)
40 -    elif basename.startswith('bilan-immo'):
41 -        pass
42 -    elif basename.startswith('bilan') and not basename.startswith('bilan_precedent'):
43 -        dest = path.replace('bilan', 'bilan2').replace('.xml', '.html')
44 -        print 'rendering bilan', dest, 'from', path
45 -        render_html.write_bilan(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, bilan_precedent)
46 +from pycompta.main import render_mod
47 +
48 +def mk_optparser():
49 +    usage = "usage: %prog [options] <source> <target>"
50 +    parser = optparse.OptionParser(usage)
51 +    parser.add_option('--debug', action='store_true', default=False,
52 +                      help=u"affiche messages de debug")
53 +    return parser
54 +
55 +def run(frmat, target, options):
56 +
57 +    if not target.endswith('/'): target += '/'
58 +    basedir = osp.dirname(target)
59 +
60 +    if frmat == 'html':
61 +        from pycompta.lib import render_html as mod
62 +    if frmat == 'json':
63 +        from pycompta.lib import render_json as mod
64 +
65 +    render_mod(mod, basedir)
66 +
67 +if __name__ == '__main__':
68 +    parser = mk_optparser()
69 +    options, args = parser.parse_args(sys.argv)
70 +
71 +    if options.debug:
72 +        import logging
73 +        logging.getLogger().setLevel(level=logging.DEBUG)
74 +
75 +    run(args[1], args[2], options)
diff --git a/lib/render_csv.py b/lib/render_csv.py
@@ -0,0 +1,43 @@
76 +# -*- coding: utf-8 -*-
77 +
78 +from pycompta.lib.render_html import fmc, format_montant
79 +
80 +SOCIETE = 'SOCIETE'
81 +EXTENSION = '.csv'
82 +
83 +def div100(string):
84 +    if len(string) > 2:
85 +        return string[:-2]+','+string[-2:]
86 +    return string
87 +
88 +def write_journal(output, journal, planc):
89 +    pass
90 +
91 +def write_grandlivre(*args):
92 +    pass
93 +
94 +def write_balance(out, glivre, planc):
95 +    glnode = glivre.getroot()
96 +    out.write('# Comptabilite %s -- Balance du %s au %s\r\n' % (
97 +            SOCIETE, glnode.get('debut'), glnode.get('fin')))
98 +    out.write('compte;libelle;report-debit;report-credit;var-debit;var-credit;debit;credit\r\n')
99 +    for compte in glivre.findall('compte'):
100 +        total = int(compte.get('solde'))
101 +        out.write(';'.join([compte.get('num'), planc.get(compte.get('num'), u''),
102 +                            div100(compte.get('report-debit')),
103 +                            div100(compte.get('report-credit')),
104 +                            div100(compte.get('var-debit')),
105 +                            div100(compte.get('var-credit')),
106 +                            div100(str(max(-total,0))),
107 +                            div100(str(max(total,0)))
108 +                            ]))
109 +        out.write('\r\n')
110 +
111 +def write_compte_resultat(*args):
112 +    pass
113 +
114 +def write_bilan(*args):
115 +    pass
116 +
117 +def write_tva(*args):
118 +    pass
diff --git a/lib/render_html.py b/lib/render_html.py
@@ -22,10 +22,12 @@
119  TRHIGHLIGHT = u'''<tr onmouseover="addElementClass(this, 'highlighted');" ''' \
120      '''onmouseout="removeElementClass(this, 'highlighted')">'''
121 
122  SOCIETE = '%soc%'
123 
124 +EXTENSION = '.html'
125 +
126  # FIXME: move javascript to lib ?
127 
128  def fmc(compte, key, empty_if_zero=True):
129      return format_montant(compte.get(key), empty_if_zero)
130 
diff --git a/lib/render_json.py b/lib/render_json.py
@@ -0,0 +1,60 @@
131 +# -*- coding: utf-8 -*-
132 +
133 +import json
134 +
135 +SOCIETE = 'SOCIETE'
136 +EXTENSION = '.json'
137 +
138 +def write_journal(output, journal, planc):
139 +    jnl = journal.getroot()
140 +    out = {}
141 +    out['title'] = u'Comptabilité %s -- Journal du %s au %s' % (SOCIETE, jnl.get('debut'), jnl.get('fin'))
142 +    out['debit'] = jnl.get('debit')
143 +    out['credit'] = jnl.get('credit')
144 +    out['ecritures'] = []
145 +    ecritures = sorted((ecr for ecr in journal.findall('ecriture')), key=lambda x: x.get('date'))
146 +    for ecr in ecritures:
147 +        item = {'num': ecr.get('e_num'),
148 +                'date': ecr.get('date'),
149 +                'label': ecr.findtext('libelle'),
150 +                'debits': [],
151 +                'credits': [],
152 +                }
153 +        for part in sorted((deb for deb in ecr.findall('debit')), key=lambda x: x.get('compte')):
154 +            item['debits'].append({'compte':part.get('compte'), 'montant': part.get('montant')})
155 +        for part in sorted((deb for deb in ecr.findall('credit')), key=lambda x: x.get('compte')):
156 +            item['credits'].append({'compte':part.get('compte'), 'montant': part.get('montant')})
157 +        txt = u''
158 +        if ecr.get('ref'):
159 +            txt += u'Cf %s <br />' % ecr.get('ref')
160 +        for reg in ecr.findall('reglement'):
161 +            txt += u'Réglement %s %s <br />' % (reg.get('type',''), reg.text)
162 +        out['ecritures'].append(item)
163 +    json.dump(out, output)
164 +
165 +def write_grandlivre(*args):
166 +    pass
167 +
168 +def write_balance(*args):
169 +    pass
170 +
171 +def write_compte_resultat(output, resultat, planc, crprecedent):
172 +    crnode = resultat.getroot()
173 +    out = {}
174 +    out['title'] = u'Comptabilité %s -- Compte résulat du %s au %s' % (
175 +        SOCIETE, crnode.get('debut'), crnode.get('fin'))
176 +    out['debut'] = crnode.get('debut')
177 +    out['fin'] = crnode.get('fin')
178 +    pnode = crnode.find('produits')
179 +    #out['produits'] = dict([ (poste.get('id'), poste) for poste in pnode.findall('poste') ])
180 +    cnode = crnode.find('charges')
181 +    #out['charges'] = dict([ (poste.get('id'), poste) for poste in cnode.findall('poste') ])
182 +    print out
183 +    json.dump(out, output)
184 +    return
185 +
186 +def write_bilan(*args):
187 +    pass
188 +
189 +def write_tva(*args):
190 +    pass
diff --git a/main.py b/main.py
@@ -10,10 +10,13 @@
191  import sys
192  import os
193  import os.path as osp
194  import optparse
195  import logging
196 +import glob
197 +import codecs
198 +import xml.etree.ElementTree as ET
199 
200  if sys.stdout.isatty():
201      from logilab.common.logging_ext import set_color_formatter
202      set_color_formatter()
203 
@@ -134,10 +137,54 @@
204      else :
205          compta_prev = None
206 
207      return compta, compta_prev
208 
209 +def planc2dict(filename):
210 +    planc = {}
211 +    for compte in ET.parse(filename).findall('.//compte'):
212 +        planc[compte.get('numero')] = compte.get('nom')
213 +    return planc
214 +
215 +def render_mod(mod, basedir, filenames=None):
216 +    planc = planc2dict(osp.join(basedir, 'plan-comptable.xml'))
217 +    #societe = ET.parse(osp.join(basedir, 'societe.xml'))
218 +
219 +    ext = mod.EXTENSION
220 +
221 +    if filenames is None:
222 +        filenames = glob.glob(osp.join(basedir,'*.xml'))
223 +
224 +    for path in filenames:
225 +        basename = osp.basename(path)
226 +        if basename.startswith('journal'):
227 +            dest = path.replace('.xml', ext)
228 +            logging.debug('rendering journal %s', dest)
229 +            mod.write_journal(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
230 +        elif basename.startswith('grand-livre'):
231 +            dest = path.replace('grand-livre', 'balance').replace('.xml', ext)
232 +            logging.debug('rendering balance %s from %s', dest, path)
233 +            mod.write_balance(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
234 +            dest = path.replace('.xml', ext)
235 +            logging.debug('rendering grand-livre %s from %s', dest, path)
236 +            mod.write_grandlivre(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
237 +            dest = path.replace('grand-livre', 'tva').replace('.xml', ext)
238 +            logging.debug('rendering tva %s from %s', dest, path)
239 +            mod.write_tva(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
240 +        elif basename.startswith('compte-resultat'):
241 +            cr_precedent = ET.parse(osp.join(basedir, 'compte-resultat_precedent.xml'))
242 +            dest = path.replace('.xml', ext)
243 +            logging.debug('rendering compte-resultat %s from %s', dest, path)
244 +            mod.write_compte_resultat(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, cr_precedent)
245 +        elif basename.startswith('bilan-immo'):
246 +            pass
247 +        elif basename.startswith('bilan') and not basename.startswith('bilan_precedent'):
248 +            bilan_precedent = ET.parse(osp.join(basedir, 'bilan_precedent.xml'))
249 +            dest = path.replace('.xml', ext)
250 +            logging.debug('rendering bilan %s from %s', dest, path)
251 +            mod.write_bilan(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, bilan_precedent)
252 +
253  ## MAIN ########################################################################
254 
255  def mk_optparser():
256      usage = "usage: %prog [options] <source> <target>"
257      parser = optparse.OptionParser(usage)
@@ -181,10 +228,11 @@
258      if options.fo or (options.pdf and options.fo is None) :
259          render.render_fo(*job)
260      if options.pdf :
261          render.render_pdf(*job)
262 
263 +# XXX move to bin/pycompta
264  def run(args):
265      """
266      Programme principal
267      """
268      # parse command-line
@@ -239,6 +287,5 @@
269          stats = stats.load('stones.prof')
270          stats.sort_stats('time', 'calls')
271          stats.print_stats(30)
272      else:
273          return run_standard(config, debut, fin, prev, options)
274 -