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