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