Merge branch 'production'
[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     file_url = os.path.join(settings.MEDIA_URL, file_name)
93     if send_name is None:
94         send_name = os.path.basename(file_name)
95
96     def signal_handler(*args, **kwargs):
97         try:
98             os.unlink(file_path)
99         except OSError as oe:
100             if oe.errno != ENOENT:
101                 raise oe
102
103     if signals:
104         for signal in signals:
105             signal.connect(signal_handler, weak=False)
106
107     def decorator(func):
108         def view(request, *args, **kwargs):
109             if not os.path.exists(file_path):
110                 func(file_path, *args, **kwargs)
111
112             if hasattr(send_name, "__call__"):
113                 name = send_name()
114             else:
115                 name = send_name
116
117             response = HttpResponse(mimetype=mime_type)
118             response['Content-Disposition'] = 'attachment; filename=%s' % name
119             with open(file_path) as f:
120                 for chunk in read_chunks(f):
121                     response.write(chunk)
122             return response
123         return view
124     return decorator