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