955f7d92d837dff94ecf6bfa20ae8b4935d80d33
[wolnelektury.git] / src / 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 from wolnelektury.utils import makedirs
12
13 logger = logging.getLogger(__name__)
14
15
16 def render_to_pdf(output_path, template, context=None, add_files=None):
17     """Renders a TeXML document into a PDF file.
18
19     :param str output_path: is where the PDF file should go
20     :param str template: is a TeXML template path
21     :param context: is context for rendering the template
22     :param dict add_files: a dictionary of additional files XeTeX will need
23     """
24
25     from io import BytesIO
26     import shutil
27     from tempfile import mkdtemp
28     import subprocess
29     import Texml.processor
30     from django.template.loader import render_to_string
31
32     rendered = render_to_string(template, context)
33     texml = BytesIO(rendered.encode('utf-8'))
34     tempdir = mkdtemp(prefix="render_to_pdf-")
35     tex_path = os.path.join(tempdir, "doc.tex")
36     with open(tex_path, 'w') as tex_file:
37         Texml.processor.process(texml, tex_file, encoding="utf-8")
38
39     if add_files:
40         for add_name, src_file in add_files.items():
41             add_path = os.path.join(tempdir, add_name)
42             if hasattr(src_file, "read"):
43                 with open(add_path, 'w') as add_file:
44                     add_file.write(add_file.read())
45             else:
46                 shutil.copy(src_file, add_path)
47
48     cwd = os.getcwd()
49     os.chdir(tempdir)
50     try:
51         subprocess.check_call(
52             ['xelatex', '-interaction=batchmode', tex_path],
53             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
54         makedirs(os.path.dirname(output_path))
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 render_to_csv(output_path, template, context=None, add_files=None):
62     """Renders a TeXML document into a PDF file.
63
64     :param str output_path: is where the PDF file should go
65     :param str template: is a TeXML template path
66     :param context: is context for rendering the template
67     :param dict add_files: a dictionary of additional files XeTeX will need
68     """
69
70     from django.template.loader import render_to_string
71
72     makedirs(os.path.dirname(output_path))
73
74     rendered = render_to_string(template, context)
75     with open(output_path, 'w') as csv_file:
76         csv_file.write(rendered.encode('utf-8'))
77
78
79 def read_chunks(f, size=8192):
80     chunk = f.read(size)
81     while chunk:
82         yield chunk
83         chunk = f.read(size)
84
85
86 def generated_file_view(file_name, mime_type, send_name=None, signals=None):
87     file_path = os.path.join(settings.MEDIA_ROOT, file_name)
88     if send_name is None:
89         send_name = os.path.basename(file_name)
90
91     def signal_handler(*args, **kwargs):
92         try:
93             os.unlink(file_path)
94         except OSError as oe:
95             if oe.errno != ENOENT:
96                 raise oe
97
98     if signals:
99         for signal in signals:
100             signal.connect(signal_handler, weak=False)
101
102     def decorator(func):
103         def view(request, *args, **kwargs):
104             if not os.path.exists(file_path):
105                 func(file_path, *args, **kwargs)
106
107             if hasattr(send_name, "__call__"):
108                 name = send_name()
109             else:
110                 name = send_name
111
112             response = HttpResponse(content_type=mime_type)
113             response['Content-Disposition'] = 'attachment; filename=%s' % name
114             with open(file_path) as f:
115                 for chunk in read_chunks(f):
116                     response.write(chunk)
117             return response
118         return view
119     return decorator