Minor fix in OAI-PMH.
[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         themes = self.parent.related_themes()
299         self.assertEqual(len(themes), 1,
300                 u"There should be child theme in parent theme counter."
301             )
302         # TODO: book_count is deprecated, update here.
303         #~ epoch = models.Tag.objects.get(slug='x-epoch')
304         #~ self.assertEqual(epoch.book_count, 1,
305                 #~ u"There should be only parent in common tag's counter."
306             #~ )
307
308     def test_child_republish(self):
309         CHILD_TEXT = """<utwor>
310         <opowiadanie>
311             <akap><begin id="m01" /><motyw id="m01">Pies, Kot</motyw>
312                 Ala ma kota<end id="m01" /></akap>
313         </opowiadanie></utwor>
314         """
315         models.Book.from_text_and_meta(
316             ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
317         self.assertEqual(
318                 list(self.client.get('/katalog/gatunek/x-genre/'
319                     ).context['object_list']),
320                 [self.parent],
321                 u"There should only be parent on common tag page."
322             )
323         pies = models.Tag.objects.get(slug='pies')
324         kot = models.Tag.objects.get(slug='kot')
325         self.assertEqual(len(self.parent.related_themes()), 2,
326                 u"There should be child themes in parent theme counter."
327             )
328         # TODO: book_count is deprecated, update here.
329         #~ epoch = models.Tag.objects.get(slug='x-epoch')
330         #~ self.assertEqual(epoch.book_count, 1,
331                 #~ u"There should only be parent in common tag's counter."
332             #~ )
333
334     def test_book_change_child(self):
335         second_child_info = BookInfoStub(
336             genre='X-Genre',
337             epoch='X-Epoch',
338             kind='Other-Kind',
339             author=PersonStub(("Joe",), "Doe"),
340             **info_args("Second Child")
341         )
342         SECOND_CHILD_TEXT = """<utwor>
343         <opowiadanie>
344             <akap><begin id="m01" /><motyw id="m01">Kot</motyw>
345                 Ala ma kota<end id="m01" /></akap>
346         </opowiadanie></utwor>
347         """
348         # Import a second child.
349         second_child = models.Book.from_text_and_meta(
350             ContentFile(SECOND_CHILD_TEXT), second_child_info)
351         # The book has only this new child now.
352         self.book_info.parts = [second_child_info.url]
353         self.book = models.Book.from_text_and_meta(
354             ContentFile(self.BOOK_TEXT), self.book_info, overwrite=True)
355
356         self.assertEqual(
357                 set(self.client.get('/katalog/gatunek/x-genre/'
358                     ).context['object_list']),
359                 set([self.parent, self.child]),
360                 u"There should be parent and old child on common tag page."
361             )
362         kot = models.Tag.objects.get(slug='kot')
363         self.assertEqual(len(self.parent.related_themes()), 1,
364                 u"There should only be new child themes in parent theme counter."
365             )
366         epoch = models.Tag.objects.get(slug='x-epoch')
367         # book_count deprecated, update test.
368         #~ self.assertEqual(epoch.book_count, 2,
369                 #~ u"There should be parent and old child in common tag's counter."
370             #~ )
371         self.assertEqual(
372                 list(self.client.get('/katalog/lektura/parent/motyw/kot/'
373                     ).context['fragments']),
374                 [second_child.fragments.all()[0]],
375                 u"There should be new child's fragments on parent's theme page."
376             )
377         self.assertEqual(
378                 list(self.client.get('/katalog/lektura/parent/motyw/pies/'
379                     ).context['fragments']),
380                 [],
381                 u"There should be no old child's fragments on parent's theme page."
382             )
383
384
385 class MultilingualBookImportTest(WLTestCase):
386     def setUp(self):
387         WLTestCase.setUp(self)
388         common_uri = WLURI.from_slug('common-slug')
389
390         self.pol_info = BookInfoStub(
391             genre='X-Genre',
392             epoch='X-Epoch',
393             kind='X-Kind',
394             author=PersonStub(("Joe",), "Doe"),
395             variant_of=common_uri,
396             **info_args(u"Książka")
397         )
398
399         self.eng_info = BookInfoStub(
400             genre='X-Genre',
401             epoch='X-Epoch',
402             kind='X-Kind',
403             author=PersonStub(("Joe",), "Doe"),
404             variant_of=common_uri,
405             **info_args("A book", "eng")
406         )
407
408     def test_multilingual_import(self):
409         BOOK_TEXT = """<utwor><opowiadanie><akap>A</akap></opowiadanie></utwor>"""
410
411         book1 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.pol_info)
412         book2 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.eng_info)
413
414         self.assertEqual(
415                 set([b.language for b in models.Book.objects.all()]),
416                 set(['pol', 'eng']),
417                 'Books imported in wrong languages.'
418             )
419
420
421 class BookImportGenerateTest(WLTestCase):
422     def setUp(self):
423         WLTestCase.setUp(self)
424         xml = path.join(path.dirname(__file__), 'files/fraszka-do-anusie.xml')
425         self.book = models.Book.from_xml_file(xml)
426
427     def test_gen_pdf(self):
428         self.book.pdf_file.build()
429         book = models.Book.objects.get(pk=self.book.pk)
430         self.assertTrue(path.exists(book.pdf_file.path))
431
432     def test_gen_pdf_parent(self):
433         """This book contains a child."""
434         xml = path.join(path.dirname(__file__), "files/fraszki.xml")
435         parent = models.Book.from_xml_file(xml)
436         parent.pdf_file.build()
437         parent = models.Book.objects.get(pk=parent.pk)
438         self.assertTrue(path.exists(parent.pdf_file.path))
439
440     def test_custom_pdf(self):
441         from catalogue.tasks import build_custom_pdf
442         out = 'test-custom.pdf'
443         absoulute_path = path.join(settings.MEDIA_ROOT, out)
444
445         if not path.exists(path.dirname(absoulute_path)):
446             makedirs(path.dirname(absoulute_path))
447
448         build_custom_pdf(self.book.id,
449             customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
450         self.assertTrue(path.exists(absoulute_path))