Merge branch 'master' of https://github.com/fnp/wolnelektury
[wolnelektury.git] / apps / reporting / utils.py
1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 from errno import ENOENT
6 import os
7 import os.path
8 from django.conf import settings
9 import logging
10 from django.http import HttpResponse
11
12 logger = logging.getLogger(__name__)
13
14
15 def render_to_pdf(output_path, template, context=None, add_files=None):
16     """Renders a TeXML document into a PDF file.
17
18     :param str output_path: is where the PDF file should go
19     :param str template: is a TeXML template path
20     :param context: is context for rendering the template
21     :param dict add_files: a dictionary of additional files XeTeX will need
22     """
23
24     from StringIO import StringIO
25     import shutil
26     from tempfile import mkdtemp
27     import subprocess
28     import Texml.processor
29     from django.template.loader import render_to_string
30
31     rendered = render_to_string(template, context)
32     texml = StringIO(rendered.encode('utf-8'))
33     tempdir = mkdtemp(prefix = "render_to_pdf-")
34     tex_path = os.path.join(tempdir, "doc.tex")
35     with open(tex_path, 'w') as tex_file:
36         Texml.processor.process(texml, tex_file, encoding="utf-8")
37
38     if add_files:
39         for add_name, src_file in add_files.items():
40             add_path = os.path.join(tempdir, add_name)
41             if hasattr(src_file, "read"):
42                 with open(add_path, 'w') as add_file:
43                     add_file.write(add_file.read())
44             else:
45                 shutil.copy(src_file, add_path)
46
47     cwd = os.getcwd()
48     os.chdir(tempdir)
49     try:
50         subprocess.check_call(['xelatex', '-interaction=batchmode', tex_path],
51             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
52         try:
53             os.makedirs(os.path.dirname(output_path))
54         except:
55             pass
56         shutil.move(os.path.join(tempdir, "doc.pdf"), output_path)
57     finally:
58         os.chdir(cwd)
59         shutil.rmtree(tempdir)
60
61
62 def read_chunks(f, size=8192):
63     chunk = f.read(size)
64     while chunk:
65         yield chunk
66         chunk = f.read(size)
67
68
69 def generated_file_view(file_name, mime_type, send_name=None, signals=None):
70     file_path = os.path.join(settings.MEDIA_ROOT, file_name)
71     file_url = os.path.join(settings.MEDIA_URL, file_name)
72     if send_name is None:
73         send_name = os.path.basename(file_name)
74
75     def signal_handler(*args, **kwargs):
76         try:
77             os.unlink(file_path)
78         except OSError as oe:
79             if oe.errno != ENOENT:
80                 raise oe
81
82     if signals:
83         for signal in signals:
84             signal.connect(signal_handler, weak=False)
85
86     def decorator(func):
87         def view(request, *args, **kwargs):
88             if not os.path.exists(file_path):
89                 func(file_path, *args, **kwargs)
90
91             if hasattr(send_name, "__call__"):
92                 name = send_name()
93             else:
94                 name = send_name
95
96             response = HttpResponse(mimetype=mime_type)
97             response['Content-Disposition'] = 'attachment; filename=%s' % name
98             with open(file_path) as f:
99                 for chunk in read_chunks(f):
100                     response.write(chunk)
101             return response
102         return view
103     return decorator