Fixes #3506: active picture links.
[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 render_to_csv(output_path, template, context=None, add_files=None):
63     """Renders a TeXML document into a PDF file.
64
65     :param str output_path: is where the PDF file should go
66     :param str template: is a TeXML template path
67     :param context: is context for rendering the template
68     :param dict add_files: a dictionary of additional files XeTeX will need
69     """
70
71     from django.template.loader import render_to_string
72
73     try:
74         os.makedirs(os.path.dirname(output_path))
75     except:
76         pass
77
78     rendered = render_to_string(template, context)
79     with open(output_path, 'w') as csv_file:
80         csv_file.write(rendered.encode('utf-8'))
81
82
83 def read_chunks(f, size=8192):
84     chunk = f.read(size)
85     while chunk:
86         yield chunk
87         chunk = f.read(size)
88
89
90 def generated_file_view(file_name, mime_type, send_name=None, signals=None):
91     file_path = os.path.join(settings.MEDIA_ROOT, file_name)
92     if send_name is None:
93         send_name = os.path.basename(file_name)
94
95     def signal_handler(*args, **kwargs):
96         try:
97             os.unlink(file_path)
98         except OSError as oe:
99             if oe.errno != ENOENT:
100                 raise oe
101
102     if signals:
103         for signal in signals:
104             signal.connect(signal_handler, weak=False)
105
106     def decorator(func):
107         def view(request, *args, **kwargs):
108             if not os.path.exists(file_path):
109                 func(file_path, *args, **kwargs)
110
111             if hasattr(send_name, "__call__"):
112                 name = send_name()
113             else:
114                 name = send_name
115
116             response = HttpResponse(content_type=mime_type)
117             response['Content-Disposition'] = 'attachment; filename=%s' % name
118             with open(file_path) as f:
119                 for chunk in read_chunks(f):
120                     response.write(chunk)
121             return response
122         return view
123     return decorator