e713fcdbc7d263068d895cc7ddc81b4f7925a19b
[wolnelektury.git] / src / social / utils.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 collections import defaultdict
6 from random import randint
7
8 from django.contrib.contenttypes.models import ContentType
9 from django.db.models import Q
10 from django.utils.functional import lazy
11 from catalogue.models import Book, Tag
12 from catalogue import utils
13 from catalogue.tasks import touch_tag
14 from social.models import Cite
15
16
17 def likes(user, work, request=None):
18     if not user.is_authenticated():
19         return False
20
21     if request is None:
22         return work.tags.filter(category='set', user=user).exists()
23
24     if not hasattr(request, 'social_likes'):
25         # tuple: unchecked, checked, liked
26         request.social_likes = defaultdict(lambda: (set(), set(), set()))
27
28     ct = ContentType.objects.get_for_model(type(work))
29     likes_t = request.social_likes[ct.pk]
30     if work.pk in likes_t[1]:
31         return work.pk in likes_t[2]
32     else:
33         likes_t[0].add(work.pk)
34
35         def _likes():
36             if likes_t[0]:
37                 ids = tuple(likes_t[0])
38                 likes_t[0].clear()
39                 likes_t[2].update(Tag.intermediary_table_model.objects.filter(
40                     content_type_id=ct.pk, tag__user_id=user.pk,
41                     object_id__in=ids
42                 ).distinct().values_list('object_id', flat=True))
43                 likes_t[1].update(ids)
44             return work.pk in likes_t[2]
45         return lazy(_likes, bool)()
46
47
48 def get_set(user, name):
49     """Returns a tag for use by the user. Creates it, if necessary."""
50     try:
51         tag = Tag.objects.get(category='set', user=user, name=name)
52     except Tag.DoesNotExist:
53         tag = Tag.objects.create(
54             category='set', user=user, name=name, slug=utils.get_random_hash(name), sort_key=name.lower())
55     except Tag.MultipleObjectsReturned:
56         # fix duplicated noname shelf
57         tags = list(Tag.objects.filter(category='set', user=user, name=name))
58         tag = tags[0]
59         for other_tag in tags[1:]:
60             for item in other_tag.items.all():
61                 Tag.objects.remove_tag(item, other_tag)
62                 Tag.objects.add_tag(item, tag)
63             other_tag.delete()
64     return tag
65
66
67 def set_sets(user, work, sets):
68     """Set tags used for given work by a given user."""
69
70     old_sets = list(work.tags.filter(category='set', user=user))
71
72     work.tags = sets + list(
73             work.tags.filter(~Q(category='set') | ~Q(user=user)))
74
75     for shelf in [shelf for shelf in old_sets if shelf not in sets]:
76         touch_tag(shelf)
77     for shelf in [shelf for shelf in sets if shelf not in old_sets]:
78         touch_tag(shelf)
79
80     # delete empty tags
81     Tag.objects.filter(category='set', user=user, items=None).delete()
82
83     if isinstance(work, Book):
84         work.update_popularity()
85
86
87 def cites_for_tags(tags):
88     """Returns a QuerySet with all Cites for books with given tags."""
89     return Cite.objects.filter(book__in=Book.tagged.with_all(tags))
90
91
92 # tag_ids is never used
93 def choose_cite(book_id=None, tag_ids=None):
94     """Choose a cite for main page, for book or for set of tags."""
95     if book_id is not None:
96         cites = Cite.objects.filter(Q(book=book_id) | Q(book__ancestor=book_id))
97     elif tag_ids is not None:
98         tags = Tag.objects.filter(pk__in=tag_ids)
99         cites = cites_for_tags(tags)
100     else:
101         cites = Cite.objects.all()
102     stickies = cites.filter(sticky=True)
103     count = len(stickies)
104     if count:
105         cites = stickies
106     else:
107         count = len(cites)
108     if count:
109         cite = cites[randint(0, count - 1)]
110     else:
111         cite = None
112     return cite
113
114
115 def get_or_choose_cite(request, book_id=None, tag_ids=None):
116     try:
117         assert request.user.is_staff
118         assert 'banner' in request.GET
119         return Cite.objects.get(pk=request.GET['banner'])
120     except (AssertionError, Cite.DoesNotExist):
121         return choose_cite(book_id, tag_ids)