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