Fundraising in PDF.
[wolnelektury.git] / src / catalogue / tests / test_tags.py
1 # This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
3 #
4 from unittest import skip
5
6 from django.core.files.base import ContentFile
7 from django.test import Client
8 from catalogue import models
9 from catalogue.test_utils import *
10
11
12 class BooksByTagTests(WLTestCase):
13     """ tests the /katalog/category/tag page for found books """
14
15     def setUp(self):
16         WLTestCase.setUp(self)
17         author = PersonStub(("Common",), "Man")
18
19         # grandchild
20         self.gchild_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Kind', author=author,
21                                         **info_args("GChild"))
22         # child
23         self.child_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Other Kind', author=author,
24                                        parts=[self.gchild_info.url],
25                                        **info_args("Child"))
26         # parent
27         self.parent_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Kind', author=author,
28                                         parts=[self.child_info.url],
29                                         **info_args("Parent"))
30
31         self.book_file = ContentFile(b'<utwor />')
32
33     def test_nonexistent_tag(self):
34         """ Looking for a non-existent tag should yield 404 """
35         self.assertEqual(404, self.client.get('/katalog/autor/czeslaw-milosz/').status_code)
36
37     def test_book_tag(self):
38         """ Looking for a book tag isn't permitted """
39         models.Book.from_text_and_meta(self.book_file, self.gchild_info)
40         self.assertEqual(404, self.client.get('/katalog/gchild/').status_code)
41
42     def test_tag_empty(self):
43         """ Tag with no books should return no books """
44         models.Book.from_text_and_meta(self.book_file, self.gchild_info)
45         models.Tag.objects.create(name='Empty tag', slug='empty', category='author')
46
47         context = self.client.get('/katalog/autor/empty/').context
48         self.assertEqual(0, len(context['object_list']))
49
50     def test_tag_eliminate(self):
51         """ Filtering by tag should only yield top-level qualifying books. """
52         for info in self.gchild_info, self.child_info, self.parent_info:
53             models.Book.from_text_and_meta(self.book_file, info)
54
55         # all three qualify
56         context = self.client.get('/katalog/gatunek/genre/').context
57         self.assertEqual([book.title for book in context['object_list']],
58                          ['Parent'])
59
60         # parent and gchild qualify, child doesn't
61         context = self.client.get('/katalog/rodzaj/kind/').context
62         self.assertEqual([book.title for book in context['object_list']],
63                          ['Parent'])
64
65         # Filtering by child's tag should yield the child
66         context = self.client.get('/katalog/rodzaj/other-kind/').context
67         self.assertEqual([book.title for book in context['object_list']],
68                          ['Child'])
69
70
71 class TagRelatedTagsTests(WLTestCase):
72     """ tests the /katalog/category/tag/ page for related tags """
73
74     def setUp(self):
75         WLTestCase.setUp(self)
76         self.client = Client()
77         author = PersonStub(("Common",), "Man")
78
79         gchild_info = BookInfoStub(author=author, genre="GchildGenre", epoch='Epoch', kind="Kind",
80                                    **info_args("GChild"))
81         child1_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
82                                    parts=[gchild_info.url],
83                                    **info_args("Child1"))
84         child2_info = BookInfoStub(author=author, genre="ChildGenre", epoch='Epoch', kind="ChildKind",
85                                    **info_args("Child2"))
86         parent_info = BookInfoStub(author=author, genre="Genre", epoch='Epoch', kind="Kind",
87                                    parts=[child1_info.url, child2_info.url],
88                                    **info_args("Parent"))
89
90         for info in gchild_info, child1_info, child2_info, parent_info:
91             book_text = """<utwor><opowiadanie><akap>
92                 <begin id="m01" />
93                     <motyw id="m01">Theme, %sTheme</motyw>
94                     Ala ma kota
95                 <end id="m01" />
96                 </akap></opowiadanie></utwor>
97                 """ % info.title
98             book = models.Book.from_text_and_meta(
99                 ContentFile(book_text.encode('utf-8')),
100                 info
101             )
102             book.save()
103
104         tag_empty = models.Tag(name='Empty tag', slug='empty', category='author')
105         tag_empty.save()
106
107     def test_empty(self):
108         """ empty tag should have no related tags """
109
110         suggested = self.client.get('/katalog/autor/empty/').context['suggested_tags']
111         self.assertEqual(suggested, [], 'tags related to empty tag')
112
113     def test_has_related(self):
114         """ related own and descendants' tags should be generated """
115
116         suggested = {
117             (t.name, t.category)
118             for t in self.client.get('/katalog/rodzaj/kind/').context['suggested_tags']
119         }
120         self.assertTrue(('Common Man', 'author') in suggested,
121                         'missing `author` related tag')
122         self.assertTrue(('Epoch', 'epoch') in suggested,
123                         'missing `epoch` related tag')
124         # TODO: this should probably be changed now.
125         self.assertFalse(any(x for x in suggested if x[1] == "kind"),
126                          "There should be no child-only related `kind` tags")
127         self.assertTrue(("Genre", 'genre') in suggested,
128                         'missing `genre` related tag')
129         # TODO: this should probably be changed now.
130         self.assertFalse(("ChildGenre", 'genre') in suggested,
131                          "There should be no child-only related `genre` tags")
132         self.assertTrue(("GchildGenre", "genre") in suggested,
133                         "missing grandchild's related tag")
134         self.assertTrue(('Theme', 'theme') in suggested,
135                         "missing related theme")
136         self.assertTrue(('Child1Theme', 'theme') in suggested,
137                          "missing child's related theme")
138         self.assertTrue(('GChildTheme', 'theme') in suggested,
139                         "missing grandchild's related theme")
140
141     def test_related_differ(self):
142         """ related tags shouldn't include filtering tags """
143
144         response = self.client.get('/katalog/rodzaj/kind/')
145         suggested = response.context['suggested_tags']
146         self.assertFalse(any(x for x in suggested if x.category == 'kind'),
147                          'filtering tag wrongly included in related')
148         suggested = {
149             (t.name, t.category)
150             for t in self.client.get(
151                     '/katalog/motyw/theme/').context['suggested_tags']
152         }
153         self.assertFalse(('Theme', 'theme') in suggested,
154                          'filtering theme wrongly included in related')
155
156     def test_parent_tag_once(self):
157         """ if parent and descendants have a common tag, count it only once """
158
159         suggested = self.client.get('/katalog/rodzaj/kind/').context['suggested_tags']
160         self.assertEqual([(tag.name, tag.count) for tag in suggested if tag.category == 'epoch'],
161                          [('Epoch', 1)],
162                          'wrong related tag epoch tag on tag page')
163
164     def test_siblings_tags_count(self):
165         """ if children have tags and parent hasn't, count the children """
166
167         suggested = self.client.get('/katalog/epoka/epoch/').context['suggested_tags']
168         kinds = [(tag.name, tag.count) for tag in suggested if tag.category == 'kind']
169         self.assertTrue(
170             ('ChildKind', 2) in kinds,
171             'wrong related kind tags on tag page'
172         )
173
174         # all occurencies of theme should be counted
175         themes = [(tag.name, tag.count) for tag in suggested if tag.category == 'theme']
176         self.assertTrue(
177             ('Theme', 4) in themes,
178             'wrong related theme count'
179         )
180
181     def test_query_child_tag(self):
182         """
183         If child and parent have a common tag, but parent isn't included
184         in the result, child should still count.
185         """
186         suggested = self.client.get('/katalog/gatunek/childgenre/').context['suggested_tags']
187         epochs = [(tag.name, tag.count) for tag in suggested if tag.category == 'epoch']
188         self.assertTrue(
189             ('Epoch', 2) in epochs,
190             'wrong related kind tags on tag page'
191         )
192
193
194 class CleanTagRelationTests(WLTestCase):
195     """ tests for tag relations cleaning after deleting things """
196
197     def setUp(self):
198         WLTestCase.setUp(self)
199         author = PersonStub(("Common",), "Man")
200
201         book_info = BookInfoStub(author=author, genre="G", epoch='E', kind="K", **info_args("Book"))
202         book_text = """<utwor><opowiadanie><akap>
203             <begin id="m01" /><motyw id="m01">Theme</motyw>Ala ma kota
204             <end id="m01" />
205             </akap></opowiadanie></utwor>
206             """
207         self.book = models.Book.from_text_and_meta(
208                 ContentFile(book_text.encode('utf-8')),
209                 book_info)
210
211     @skip('Not implemented and not priority')
212     def test_delete_objects(self):
213         """ there should be no related tags left after deleting some objects """
214
215         models.Book.objects.all().delete()
216         suggested = self.client.get('/katalog/rodzaj/k/').context['suggested_tags']
217         self.assertEqual(suggested, [])
218         self.assertEqual(models.Fragment.objects.all().count(), 0,
219                          "orphaned fragments left")
220         self.assertEqual(models.Tag.intermediary_table_model.objects.all().count(), 0,
221                          "orphaned TagRelation objects left")
222
223     def test_deleted_tag(self):
224         """ there should be no tag relations left after deleting tags """
225
226         models.Tag.objects.all().delete()
227         self.assertEqual(len(self.book.related_themes()), 0)
228         self.assertEqual(models.Tag.intermediary_table_model.objects.all().count(), 0,
229                          "orphaned TagRelation objects left")
230
231
232 class TestIdenticalTag(WLTestCase):
233
234     def setUp(self):
235         WLTestCase.setUp(self)
236         author = PersonStub((), "Tag")
237
238         self.book_info = BookInfoStub(author=author, genre="tag", epoch='tag', kind="tag", **info_args("tag"))
239         self.book_text = """<utwor>
240             <opowiadanie>
241             <akap>
242                 <begin id="m01" /><motyw id="m01">tag</motyw>Ala ma kota<end id="m01" />
243             </akap>
244             </opowiadanie>
245             </utwor>
246         """
247
248     def test_book_tags(self):
249         """ there should be all related tags in relevant categories """
250         book = models.Book.from_text_and_meta(
251                 ContentFile(self.book_text.encode('utf-8')),
252                 self.book_info)
253
254         related_themes = book.related_themes()
255         for category in 'author', 'kind', 'genre', 'epoch':
256             self.assertTrue('tag' in book.tags.filter(category=category).values_list('slug', flat=True),
257                             'missing related tag for %s' % category)
258         self.assertTrue('tag' in [tag.slug for tag in related_themes])
259
260     def test_qualified_url(self):
261         models.Book.from_text_and_meta(
262                 ContentFile(self.book_text.encode('utf-8')),
263                 self.book_info)
264         categories = {'author': 'autor', 'theme': 'motyw', 'epoch': 'epoka', 'kind': 'rodzaj', 'genre': 'gatunek'}
265         for cat, localcat in categories.items():
266             if cat == 'theme': continue
267             context = self.client.get('/katalog/%s/tag/' % localcat).context
268             self.assertEqual(1, len(context['object_list']))
269             self.assertNotEqual([], context['suggested_tags'])
270             self.assertFalse(any(t for t in context['suggested_tags'] if t.category == cat))
271
272
273 class BookTagsTests(WLTestCase):
274     """ tests the /katalog/lektura/book/ page for related tags """
275
276     def setUp(self):
277         WLTestCase.setUp(self)
278         author1 = PersonStub(("Common",), "Man")
279         author2 = PersonStub(("Jim",), "Lazy")
280
281         child_info = BookInfoStub(authors=(author1, author2), genre="ChildGenre", epoch='Epoch', kind="ChildKind",
282                                   **info_args("Child"))
283         parent_info = BookInfoStub(author=author1, genre="Genre", epoch='Epoch', kind="Kind",
284                                    parts=[child_info.url],
285                                    **info_args("Parent"))
286
287         for info in child_info, parent_info:
288             book_text = """<utwor><opowiadanie><akap>
289                 <begin id="m01" />
290                     <motyw id="m01">Theme, %sTheme</motyw>
291                     Ala ma kota
292                 <end id="m01" />
293                 </akap></opowiadanie></utwor>
294                 """ % info.title
295             models.Book.from_text_and_meta(
296                     ContentFile(book_text.encode('utf-8')),
297                     info)
298
299     def test_book_tags(self):
300         """ book should have own tags and whole tree's themes """
301
302         book = models.Book.objects.get(slug='parent')
303         related_themes = book.related_themes()
304
305         self.assertEqual([t.slug for t in book.authors()],
306                          ['common-man'])
307         self.assertEqual([t.slug for t in book.tags.filter(category='kind')],
308                          ['kind'])
309         self.assertEqual([(tag.name, tag.count) for tag in related_themes],
310                          [('Theme', 2), ('ChildTheme', 1), ('ParentTheme', 1)])