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