08d5243eb88153b5d4a659685d4e5c5757d2e292
[wolnelektury.git] / apps / catalogue / tests / book_import.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 django.conf import settings
6
7 from django.core.files.base import ContentFile
8 from catalogue.test_utils import *
9 from catalogue import models
10 from librarian import WLURI
11
12 from nose.tools import raises
13 from os import path, makedirs
14
15 class BookImportLogicTests(WLTestCase):
16
17     def setUp(self):
18         WLTestCase.setUp(self)
19         self.book_info = BookInfoStub(
20             url=WLURI.from_slug(u"default-book"),
21             about=u"http://wolnelektury.pl/example/URI/default_book",
22             title=u"Default Book",
23             author=PersonStub(("Jim",), "Lazy"),
24             kind="X-Kind",
25             genre="X-Genre",
26             epoch="X-Epoch",
27             language=u"pol",
28         )
29
30         self.expected_tags = [
31            ('author', 'jim-lazy'),
32            ('genre', 'x-genre'),
33            ('epoch', 'x-epoch'),
34            ('kind', 'x-kind'),
35         ]
36         self.expected_tags.sort()
37
38     def test_empty_book(self):
39         BOOK_TEXT = "<utwor />"
40         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
41
42         self.assertEqual(book.title, "Default Book")
43         self.assertEqual(book.slug, "default-book")
44         self.assert_(book.parent is None)
45         self.assertFalse(book.has_html_file())
46
47         # no fragments generated
48         self.assertEqual(book.fragments.count(), 0)
49
50         # TODO: this should be filled out probably...
51         self.assertEqual(book.wiki_link, '')
52         self.assertEqual(book.gazeta_link, '')
53         self.assertEqual(book.description, '')
54
55         tags = [ (tag.category, tag.slug) for tag in book.tags ]
56         tags.sort()
57
58         self.assertEqual(tags, self.expected_tags)
59
60     def test_not_quite_empty_book(self):
61         """ Not empty, but without any real text.
62
63         Should work like any other non-empty book.
64         """
65
66         BOOK_TEXT = """<utwor>
67         <liryka_l>
68             <nazwa_utworu>Nic</nazwa_utworu>
69         </liryka_l></utwor>
70         """
71
72         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
73         self.assertTrue(book.has_html_file())
74
75     def test_book_with_fragment(self):
76         BOOK_TEXT = """<utwor>
77         <opowiadanie>
78             <akap><begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" /></akap>
79         </opowiadanie></utwor>
80         """
81
82         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
83         self.assertTrue(book.has_html_file())
84
85         self.assertEqual(book.fragments.count(), 1)
86         self.assertEqual(book.fragments.all()[0].text, u'<p class="paragraph">Ala ma kota</p>\n')
87
88         self.assert_(('theme', 'love') in [ (tag.category, tag.slug) for tag in book.fragments.all()[0].tags ])
89
90     def test_book_with_empty_theme(self):
91         """ empty themes should be ignored """
92
93         BOOK_TEXT = """<utwor>
94         <opowiadanie>
95             <akap><begin id="m01" /><motyw id="m01"> , Love , , </motyw>Ala ma kota<end id="m01" /></akap>
96         </opowiadanie></utwor>
97         """
98
99         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
100         self.assert_([('theme', 'love')],
101                          [ (tag.category, tag.slug) for tag in book.fragments.all()[0].tags.filter(category='theme') ])
102
103     def test_book_with_no_theme(self):
104         """ fragments with no themes shouldn't be created at all """
105
106         BOOK_TEXT = """<utwor>
107         <opowiadanie>
108             <akap><begin id="m01" /><motyw id="m01"></motyw>Ala ma kota<end id="m01" /></akap>
109         </opowiadanie></utwor>
110         """
111
112         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
113         self.assertEqual(book.fragments.count(), 0)
114         self.assertEqual(book.tags.filter(category='theme').count(), 0)
115
116     @raises(ValueError)
117     def test_book_with_invalid_slug(self):
118         """ Book with invalid characters in slug shouldn't be imported """
119         self.book_info.url = WLURI.from_slug(u"default_book")
120         BOOK_TEXT = "<utwor />"
121         models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
122
123     def test_book_replace_title(self):
124         BOOK_TEXT = """<utwor />"""
125         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
126         self.book_info.title = u"Extraordinary"
127         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
128
129         tags = [ (tag.category, tag.slug) for tag in book.tags ]
130         tags.sort()
131
132         self.assertEqual(tags, self.expected_tags)
133
134     def test_book_replace_author(self):
135         BOOK_TEXT = """<utwor />"""
136         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
137         self.book_info.author = PersonStub(("Hans", "Christian"), "Andersen")
138         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
139
140         tags = [ (tag.category, tag.slug) for tag in book.tags ]
141         tags.sort()
142
143         self.expected_tags.remove(('author', 'jim-lazy'))
144         self.expected_tags.append(('author', 'hans-christian-andersen'))
145         self.expected_tags.sort()
146
147         self.assertEqual(tags, self.expected_tags)
148
149         # the old tag shouldn't disappear
150         models.Tag.objects.get(slug="jim-lazy", category="author")
151
152     def test_book_remove_fragment(self):
153         BOOK_TEXT = """<utwor>
154         <opowiadanie>
155             <akap>
156                 <begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" />
157                 <begin id="m02" /><motyw id="m02">Hatred</motyw>To kot Ali<end id="m02" />
158             </akap>
159         </opowiadanie></utwor>
160         """
161         BOOK_TEXT_AFTER = """<utwor>
162         <opowiadanie>
163             <akap>
164                 <begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" />
165                 To kot Ali
166             </akap>
167         </opowiadanie></utwor>
168         """
169
170         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
171         self.assertEqual(book.fragments.count(), 2)
172         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT_AFTER), self.book_info, overwrite=True)
173         self.assertEqual(book.fragments.count(), 1)
174
175     def test_multiple_tags(self):
176         BOOK_TEXT = """<utwor />"""
177         self.book_info.authors = self.book_info.author, PersonStub(("Joe",), "Dilligent"),
178         self.book_info.kinds = self.book_info.kind, 'Y-Kind',
179         self.book_info.genres = self.book_info.genre, 'Y-Genre',
180         self.book_info.epochs = self.book_info.epoch, 'Y-Epoch',
181
182         self.expected_tags.extend([
183            ('author', 'joe-dilligent'),
184            ('genre', 'y-genre'),
185            ('epoch', 'y-epoch'),
186            ('kind', 'y-kind'),
187         ])
188         self.expected_tags.sort()
189
190         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
191         tags = [ (tag.category, tag.slug) for tag in book.tags ]
192         tags.sort()
193
194         self.assertEqual(tags, self.expected_tags)
195
196
197 class ChildImportTests(WLTestCase):
198
199     def setUp(self):
200         WLTestCase.setUp(self)
201         self.child_info = BookInfoStub(
202             genre='X-Genre',
203             epoch='X-Epoch',
204             kind='X-Kind',
205             author=PersonStub(("Joe",), "Doe"),
206             **info_args("Child")
207         )
208
209         self.parent_info = BookInfoStub(
210             genre='X-Genre',
211             epoch='X-Epoch',
212             kind='X-Kind',
213             author=PersonStub(("Jim",), "Lazy"),
214             parts=[self.child_info.url],
215             **info_args("Parent")
216         )
217
218     def test_child(self):
219         TEXT = """<utwor />"""
220         child = models.Book.from_text_and_meta(ContentFile(TEXT), self.child_info)
221         parent = models.Book.from_text_and_meta(ContentFile(TEXT), self.parent_info)
222         author = parent.tags.get(category='author')
223         books = self.client.get(author.get_absolute_url()).context['object_list']
224         self.assertEqual(len(books), 1,
225                         "Only parent book should be visible on author's page")
226
227     def test_child_replace(self):
228         PARENT_TEXT = """<utwor />"""
229         CHILD_TEXT = """<utwor>
230         <opowiadanie>
231             <akap><begin id="m01" /><motyw id="m01">Pies</motyw>Ala ma kota<end id="m01" /></akap>
232         </opowiadanie></utwor>
233         """
234         child = models.Book.from_text_and_meta(ContentFile(CHILD_TEXT), self.child_info)
235         parent = models.Book.from_text_and_meta(ContentFile(PARENT_TEXT), self.parent_info)
236         CHILD_TEXT = """<utwor>
237         <opowiadanie>
238             <akap><begin id="m01" /><motyw id="m01">Kot</motyw>Ala ma kota<end id="m01" /></akap>
239         </opowiadanie></utwor>
240         """
241         child = models.Book.from_text_and_meta(ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
242         themes = parent.related_themes()
243         self.assertEqual(['Kot'], [tag.name for tag in themes],
244                         'wrong related theme list')
245
246
247 class TreeImportTest(WLTestCase):
248     def setUp(self):
249         WLTestCase.setUp(self)
250         self.child_info = BookInfoStub(
251             genre='X-Genre',
252             epoch='X-Epoch',
253             kind='X-Kind',
254             author=PersonStub(("Joe",), "Doe"),
255             **info_args("Child")
256         )
257         self.CHILD_TEXT = """<utwor>
258         <opowiadanie>
259             <akap><begin id="m01" /><motyw id="m01">Pies</motyw>
260                 Ala ma kota<end id="m01" /></akap>
261         </opowiadanie></utwor>
262         """
263         self.child = models.Book.from_text_and_meta(
264             ContentFile(self.CHILD_TEXT), self.child_info)
265
266         self.book_info = BookInfoStub(
267             genre='X-Genre',
268             epoch='X-Epoch',
269             kind='X-Kind',
270             author=PersonStub(("Joe",), "Doe"),
271             parts=[self.child_info.url],
272             **info_args("Book")
273         )
274         self.BOOK_TEXT = """<utwor />"""
275         self.book = models.Book.from_text_and_meta(
276             ContentFile(self.BOOK_TEXT), self.book_info)
277
278         self.parent_info = BookInfoStub(
279             genre='X-Genre',
280             epoch='X-Epoch',
281             kind='X-Kind',
282             author=PersonStub(("Jim",), "Lazy"),
283             parts=[self.book_info.url],
284             **info_args("Parent")
285         )
286         self.PARENT_TEXT = """<utwor />"""
287         self.parent = models.Book.from_text_and_meta(
288             ContentFile(self.PARENT_TEXT), self.parent_info)
289
290     def test_ok(self):
291         self.assertEqual(
292                 list(self.client.get('/katalog/gatunek/x-genre/'
293                     ).context['object_list']),
294                 [self.parent],
295                 u"There should be only parent on common tag page."
296             )
297         pies = models.Tag.objects.get(slug='pies')
298         self.assertEqual(self.parent.theme_counter, {pies.pk: 1},
299                 u"There should be child theme in parent theme counter."
300             )
301         epoch = models.Tag.objects.get(slug='x-epoch')
302         self.assertEqual(epoch.book_count, 1,
303                 u"There should be only parent in common tag's counter."
304             )
305
306     def test_child_republish(self):
307         CHILD_TEXT = """<utwor>
308         <opowiadanie>
309             <akap><begin id="m01" /><motyw id="m01">Pies, Kot</motyw>
310                 Ala ma kota<end id="m01" /></akap>
311         </opowiadanie></utwor>
312         """
313         models.Book.from_text_and_meta(
314             ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
315         self.assertEqual(
316                 list(self.client.get('/katalog/gatunek/x-genre/'
317                     ).context['object_list']),
318                 [self.parent],
319                 u"There should only be parent on common tag page."
320             )
321         pies = models.Tag.objects.get(slug='pies')
322         kot = models.Tag.objects.get(slug='kot')
323         self.assertEqual(self.parent.theme_counter, {pies.pk: 1, kot.pk: 1},
324                 u"There should be child themes in parent theme counter."
325             )
326         epoch = models.Tag.objects.get(slug='x-epoch')
327         self.assertEqual(epoch.book_count, 1,
328                 u"There should only be parent in common tag's counter."
329             )
330
331     def test_book_change_child(self):
332         second_child_info = BookInfoStub(
333             genre='X-Genre',
334             epoch='X-Epoch',
335             kind='Other-Kind',
336             author=PersonStub(("Joe",), "Doe"),
337             **info_args("Second Child")
338         )
339         SECOND_CHILD_TEXT = """<utwor>
340         <opowiadanie>
341             <akap><begin id="m01" /><motyw id="m01">Kot</motyw>
342                 Ala ma kota<end id="m01" /></akap>
343         </opowiadanie></utwor>
344         """
345         # Import a second child.
346         second_child = models.Book.from_text_and_meta(
347             ContentFile(SECOND_CHILD_TEXT), second_child_info)
348         # The book has only this new child now.
349         self.book_info.parts = [second_child_info.url]
350         self.book = models.Book.from_text_and_meta(
351             ContentFile(self.BOOK_TEXT), self.book_info, overwrite=True)
352
353         self.assertEqual(
354                 set(self.client.get('/katalog/gatunek/x-genre/'
355                     ).context['object_list']),
356                 set([self.parent, self.child]),
357                 u"There should be parent and old child on common tag page."
358             )
359         kot = models.Tag.objects.get(slug='kot')
360         self.assertEqual(self.parent.theme_counter, {kot.pk: 1},
361                 u"There should only be new child themes in parent theme counter."
362             )
363         epoch = models.Tag.objects.get(slug='x-epoch')
364         self.assertEqual(epoch.book_count, 2,
365                 u"There should be parent and old child in common tag's counter."
366             )
367         self.assertEqual(
368                 list(self.client.get('/katalog/lektura/parent/motyw/kot/'
369                     ).context['fragments']),
370                 [second_child.fragments.all()[0]],
371                 u"There should be new child's fragments on parent's theme page."
372             )
373         self.assertEqual(
374                 list(self.client.get('/katalog/lektura/parent/motyw/pies/'
375                     ).context['fragments']),
376                 [],
377                 u"There should be no old child's fragments on parent's theme page."
378             )
379
380
381 class MultilingualBookImportTest(WLTestCase):
382     def setUp(self):
383         WLTestCase.setUp(self)
384         common_uri = WLURI.from_slug('common-slug')
385
386         self.pol_info = BookInfoStub(
387             genre='X-Genre',
388             epoch='X-Epoch',
389             kind='X-Kind',
390             author=PersonStub(("Joe",), "Doe"),
391             variant_of=common_uri,
392             **info_args(u"Książka")
393         )
394
395         self.eng_info = BookInfoStub(
396             genre='X-Genre',
397             epoch='X-Epoch',
398             kind='X-Kind',
399             author=PersonStub(("Joe",), "Doe"),
400             variant_of=common_uri,
401             **info_args("A book", "eng")
402         )
403
404     def test_multilingual_import(self):
405         BOOK_TEXT = """<utwor><opowiadanie><akap>A</akap></opowiadanie></utwor>"""
406
407         book1 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.pol_info)
408         book2 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.eng_info)
409
410         self.assertEqual(
411                 set([b.language for b in models.Book.objects.all()]),
412                 set(['pol', 'eng']),
413                 'Books imported in wrong languages.'
414             )
415
416
417 class BookImportGenerateTest(WLTestCase):
418     def setUp(self):
419         WLTestCase.setUp(self)
420         xml = path.join(path.dirname(__file__), 'files/fraszka-do-anusie.xml')
421         self.book = models.Book.from_xml_file(xml)
422
423     def test_gen_pdf(self):
424         self.book.pdf_file.build()
425         book = models.Book.objects.get(pk=self.book.pk)
426         self.assertTrue(path.exists(book.pdf_file.path))
427
428     def test_gen_pdf_parent(self):
429         """This book contains a child."""
430         xml = path.join(path.dirname(__file__), "files/fraszki.xml")
431         parent = models.Book.from_xml_file(xml)
432         parent.pdf_file.build()
433         parent = models.Book.objects.get(pk=parent.pk)
434         self.assertTrue(path.exists(parent.pdf_file.path))
435
436     def test_custom_pdf(self):
437         from catalogue.tasks import build_custom_pdf
438         from catalogue.utils import get_dynamic_path
439         out = get_dynamic_path(None, 'test-custom', ext='pdf')
440         absoulute_path = path.join(settings.MEDIA_ROOT, out)
441
442         if not path.exists(path.dirname(absoulute_path)):
443             makedirs(path.dirname(absoulute_path))
444
445         build_custom_pdf(self.book.id,
446             customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
447         self.assertTrue(path.exists(absoulute_path))