weasyprint support - in progress
[edumed.git] / catalogue / publish.py
1 # -*- coding: utf-8
2 from django.core.files import File
3 from django.core.urlresolvers import reverse
4 from librarian import DocProvider, IOFile
5 from librarian.pyhtml import EduModuleFormat
6 from librarian.pypdf import EduModulePDFFormat
7 from librarian.weasy import EduModuleWeasyFormat
8 from .models import Lesson, Attachment
9 from fnpdjango.utils.text.slughifi import slughifi
10
11
12 # TODO: Using sorl.thumbnail for now,
13 # but this should be done in Librarian,
14 # directly using convert or PIL as a fallback.
15 def get_image(src_img_path, width=None, default_width=1600, formats=('PNG', 'JPEG', 'GIF')):
16     """ Returns an object with `url` and `storage` attributes,
17         or None if using the original image is OK.
18     """
19
20     from PIL import Image
21     from sorl.thumbnail import get_thumbnail
22
23     # Does it need converting?
24     # Yes, if width is given explicitly.
25     convert = width is not None
26     if not convert:
27         # Looks like it doesn't need converting.
28         # But let's try opening it and checking its type.
29         try:
30             simg = Image.open(src_img_path)
31         except IOError:
32             # It doesn't look like image,
33             # but maybe it's convertable to one.
34             convert = True
35         else:
36             if simg.format not in formats:
37                 # It's an image, but it's in some weird format.
38                 convert = True
39                 width = simg.size[0]
40
41     if convert:
42         if width is None:
43             width = default_width
44         try:
45             return get_thumbnail(src_img_path, '%sx%s' % (width, 10*width))
46         except:
47             # hard to predict what solr raises on invalid image
48             return None
49     else:
50         return None
51
52
53 class HtmlFormat(EduModuleFormat):
54     IMAGE_FORMATS = ('PNG', 'JPEG', 'GIF')
55     DEFAULT_IMAGE_WIDTH = 1600
56
57     def find_attachment(self, slug, fmt):
58         lesson_slug = self.wldoc.book_info.url.slug
59         try:
60             # If already saved, use it.
61             att = Attachment.objects.get(lesson__slug=lesson_slug,
62                                          slug=slug, ext=fmt)
63         except Attachment.DoesNotExist, e:
64             # If attached to source IOFile, save now.
65             att_name = "%s.%s" % (slug, fmt)
66             try:
67                 att_file = self.wldoc.source.attachments[att_name]
68             except KeyError:
69                 print u"ATTACHMENT MISSING:", att_name
70                 raise self.MaterialNotFound()
71             else:
72                 lesson = Lesson.objects.get(slug=lesson_slug)
73                 att = lesson.attachment_set.create(slug=slug, ext=fmt)
74                 att.file.save(att_name, File(att_file.get_file()))
75                 return att
76         else:
77             return att
78
79     def url_for_material(self, slug, fmt):
80         return self.find_attachment(slug, fmt).file.url
81
82     def url_for_image(self, slug, fmt, width=None):
83         try:
84             src_img = self.find_attachment(slug, fmt).file
85         except self.MaterialNotFound:
86             return ''
87         img = get_image(src_img.path, width, self.DEFAULT_IMAGE_WIDTH, self.IMAGE_FORMATS)
88         return (img or src_img).url
89
90     def text_to_anchor(self, text):
91         return slughifi(text)
92
93     def get_forma_url(self, forma):
94         return '%s#%s' % (
95             reverse('catalogue_lesson', args=['metody']),
96             self.text_to_anchor(forma)
97         )
98
99     def get_help_url(self, naglowek):
100         return '%s%s#%s' % (
101             '//edukacjamedialna.edu.pl',
102             reverse('info', args=['jak-korzystac/']),
103             self.naglowek_to_anchor(naglowek)
104         )
105
106
107 class PdfFormat(EduModulePDFFormat):
108     IMAGE_FORMATS = ('PNG', 'JPEG', 'GIF')
109     DEFAULT_IMAGE_WIDTH = 1600
110
111     def get_image(self, name):
112         src_img = super(PdfFormat, self).get_image(name)
113         img = get_image(
114             src_img.get_filename(),
115             default_width=self.DEFAULT_IMAGE_WIDTH,
116             formats=self.IMAGE_FORMATS)
117         if img:
118             return IOFile.from_filename(img.storage.path(img))
119         else:
120             return src_img
121
122
123 class WeasyFormat(EduModuleWeasyFormat):
124     IMAGE_FORMATS = ('PNG', 'JPEG', 'GIF')
125     DEFAULT_IMAGE_WIDTH = 1600
126
127     def find_attachment(self, slug, fmt):
128         lesson_slug = self.wldoc.book_info.url.slug
129         try:
130             # If already saved, use it.
131             att = Attachment.objects.get(lesson__slug=lesson_slug,
132                                          slug=slug, ext=fmt)
133         except Attachment.DoesNotExist, e:
134             # If attached to source IOFile, save now.
135             att_name = "%s.%s" % (slug, fmt)
136             try:
137                 att_file = self.wldoc.source.attachments[att_name]
138             except KeyError:
139                 print u"ATTACHMENT MISSING:", att_name
140                 raise self.MaterialNotFound()
141             else:
142                 lesson = Lesson.objects.get(slug=lesson_slug)
143                 att = lesson.attachment_set.create(slug=slug, ext=fmt)
144                 att.file.save(att_name, File(att_file.get_file()))
145                 return att
146         else:
147             return att
148
149     def url_for_material(self, slug, fmt):
150         return self.find_attachment(slug, fmt).file.url
151
152     def image(self, slug, fmt, width=None):
153         try:
154             src_img = self.find_attachment(slug, fmt).file
155         except self.MaterialNotFound:
156             return None
157         img = get_image(src_img.path, width, self.DEFAULT_IMAGE_WIDTH, self.IMAGE_FORMATS)
158         return img or src_img
159
160     def url_for_image(self, slug, fmt, image=None, **kwargs):
161         img = image or self.image(slug, fmt, **kwargs)
162         if img:
163             return img.url
164         else:
165             return ''
166
167     def text_to_anchor(self, text):
168         return slughifi(text)
169
170
171 class OrmDocProvider(DocProvider):
172     def by_slug(self, slug):
173         """Should return a file-like object with a WL document XML."""
174         return IOFile.from_filename(Lesson.objects.get(slug=slug).xml_file.path)