Improving status report, changing to rsync by adding function publish for output.
[treecutter.git] / src / tree-cutter.py
1 #!/usr/bin/python
2 import os
3 import fnmatch
4 import subprocess
5 import amara
6 import re
7 import tempfile
8 import errno
9 import time
10 import argparse
11 from amara import bindery
12 from amara.xslt import transform
13 from Cheetah.Template import Template
14
15 parser = argparse.ArgumentParser(description='Process docbook article tree.')
16 parser.add_argument('--style', nargs='?',
17                     default=os.path.dirname(os.getcwd())+'/style/default/')
18 parser.add_argument('--output', nargs='?',
19                     default=os.path.dirname(os.getcwd())+'/htdocs/')
20 args = parser.parse_args()
21
22 style_xslt = args.style+"docbook.xsl"
23 style_tmpl = args.style+"index.en.html.tmpl"
24 outputdir = args.output
25
26 valid_scripts = ['.py','.pl']
27 MAXLEVEL = 10000
28
29 def mkdir_p(path):
30     try:
31         os.makedirs(path)
32     except OSError as exc: # Python >2.5
33         if exc.errno == errno.EEXIST:
34             pass
35         else: raise
36
37 def publish(src,target):
38     cmd = ["rsync","-a",src,target]
39     retcode = subprocess.call(cmd)
40     if retcode:
41         print 'Error: '+' '.join(cmd)+' Returncode ['+str(retcode)+']'
42
43 def generateSitemap():
44   sitemap = []
45   try:
46     sfile = open('sitemap.txt')
47     flist = sfile.read().split()
48     sfile.close()
49     for f in flist:
50       sitemap.append(dict(link=f))
51   except IOError, what_error:
52     print 'Sitemap missing - generating one.'
53   for dirname, dirnames, filenames in os.walk('.'):
54     for filename in filenames:
55       if fnmatch.fnmatch(filename, '*.xml'):
56         xfile = os.path.join(dirname,filename)
57         doc = bindery.parse(xfile,
58                             prefixes={u'db': u'http://docbook.org/ns/docbook',
59                                       u'xi': u'http://www.w3.org/2001/XInclude'})
60         title = doc.xml_select(u'/db:article/db:info/db:title')
61         menu  = doc.xml_select(u'/db:article/db:info/db:titleabbrev')
62         code  = doc.xml_select(u"//xi:include[@parse='text']")
63         exe = 0
64         for c in code:
65           (p, ext) = os.path.splitext(c.href)
66           if ext in valid_scripts:
67             exe = 1
68
69         if title and menu:
70           found = 0
71           base = xfile.split('.')[1]
72           link = base.replace('index','')
73           level = len(filter(None,re.split(r'(/\w*/)',link)))
74           page = dict(title=unicode(doc.article.info.title),
75                       menu=unicode(doc.article.info.titleabbrev),
76                       output=os.path.join(dirname,
77                                           filename.replace('xml','html')),
78                       exe=exe,
79                       file=xfile,
80                       level=level)
81           for l in sitemap:
82             if l['link'] == link:
83               found = 1
84               l.update(page)
85           if not found:
86             print "adding "+link+" to sitemap"
87             dd = dict(link=link)
88             dd.update(page)
89             sitemap.append(dd)
90   sfile = open('sitemap.txt','w')
91   for l in sitemap:
92     sfile.write(l['link']+'\n')
93   sfile.close()
94   return sitemap
95
96 def expandXincludeTxt(page):
97   doc = bindery.parse(page['file'],
98                       prefixes={u'db': u'http://docbook.org/ns/docbook',
99                                 u'xi': u'http://www.w3.org/2001/XInclude'})
100   if page['exe']:
101     code  = doc.xml_select(u"//xi:include[@parse='text']")
102     for c in code:
103       (p, ext) = os.path.splitext(c.href)
104       if ext in valid_scripts:
105         exe = os.path.join(os.path.abspath(c.href))
106         xml = subprocess.Popen([exe],stdout=subprocess.PIPE)
107         xstr = bindery.parse(str(xml.stdout.read()))
108         id = c.xml_index_on_parent
109         for x in xstr.xml_children:
110           c.xml_parent.xml_insert(id,x)
111         c.xml_parent.xml_remove(c)
112   return doc
113
114 def xsltConvert(doc):
115 #  amara can not handle the docbook stylesheets
116 #  xmlarticle = transform(doc,style_xslt)
117   cwd = os.getcwd()
118   rundir = os.path.dirname(page['file'])
119   os.chdir(rundir)
120   infile  = os.path.basename(tempfile.mktemp())
121   outfile = tempfile.mktemp()
122   tfi = open(infile,'w')
123   tfi.write(doc.xml_encode())
124   tfi.close()
125 #  cmd = ["saxon-xslt-xinclude","-o",outfile,infile,style_xslt]
126   cmd = ["xsltproc","--xinclude","--output",outfile,style_xslt,infile]
127   retcode = subprocess.call(cmd)
128   if retcode:
129     print 'Error: '+' '.join(cmd)+' Returncode ['+str(retcode)+']'
130   tfo = open(outfile,'r')
131   result = tfo.read()
132   tfo.close()
133   os.remove(infile)
134   os.remove(outfile)
135   os.chdir(cwd)
136   return result
137
138 def genMenu(page,sitemap,slevel,elevel):
139   title = None
140   sm = []
141   if elevel == MAXLEVEL or elevel == 1:
142     sm = sitemap
143   else:
144     idx = sitemap.index(page)
145     while (sitemap[idx]['level'] == page['level']):
146       idx = idx-1
147     title = sitemap[idx]['menu']
148     idx = idx+1
149     while (idx < len(sitemap) and sitemap[idx]['level'] == page['level']):
150       sm.append(sitemap[idx])
151       idx = idx+1
152   oldlevel = slevel
153   html = '<ul>\n'
154   for p in sm:
155     if slevel > p['level'] or elevel < p['level']:
156       continue
157     if not title and p['link'] == '/':
158       title = p['menu']
159
160     if oldlevel < p['level']:
161       html+='<ul>\n'
162     elif oldlevel > p['level']:
163       if p['link'][-1] == '/':
164         html+='</li>\n'
165       html+='</ul>\n</li>\n'
166     if page == p:
167       html+='<li><a href="%s">[%s]</a>' % (p['link'],p['menu'])
168     else:
169       html+='<li><a href="%s">%s</a>' % (p['link'],p['menu'])
170     if p['link'][-1] != '/' or p['link'] == '/':
171         html+='</li>\n'
172     oldlevel = p['level']
173   html+='</ul>\n'
174   return (html,title)
175
176 def writeToTemplate(page,doc,sitemap):
177   (menu,menuname) = genMenu(page,sitemap,1,MAXLEVEL)
178   (levelmenu,levelname) = genMenu(page,sitemap,page['level'],page['level'])
179   template = Template(file=style_tmpl,
180                       searchList=[{'title':page['title']},
181                                   {'menu':menu},
182                                   {'article':doc},
183                                   {'levelmenu':levelmenu},
184                                   {'levelname':levelname}])
185   outfile = args.output+page['output']
186   out = open('result', 'w')
187   out.write(str(template))
188   out.close()
189   publish('result',outfile)
190   os.remove('result')
191
192 sitemap = generateSitemap()
193 for page in sitemap:
194   t1 = time.time()
195   print "Page : %-30s %30s" % (page['link'],
196                       time.ctime(os.stat(page['file']).st_mtime)),
197   doc = expandXincludeTxt(page)
198   pubdoc = xsltConvert(doc)
199   writeToTemplate(page,pubdoc,sitemap)
200   publish(args.style+"css", args.output)
201   publish(args.style+"images",args.output)
202   t2 = time.time()
203   print "[%5.2f s]" % (round(t2-t1,2))