~widelands-dev/widelands-website/django_staticfiles

« back to all changes in this revision

Viewing changes to djangoratings/managers.py

  • Committer: franku
  • Date: 2018-11-21 17:54:32 UTC
  • mfrom: (508.1.1 widelands)
  • Revision ID: somal@arcor.de-20181121175432-8rc3h0332xmgmma4
merged trunk, resolved conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from django.db.models import Manager
2
 
from django.db.models.query import QuerySet
3
 
 
4
 
from django.contrib.contenttypes.models import ContentType
5
 
import itertools
6
 
 
7
 
 
8
 
class VoteQuerySet(QuerySet):
9
 
 
10
 
    def delete(self, *args, **kwargs):
11
 
        """Handles updating the related `votes` and `score` fields attached to
12
 
        the model."""
13
 
        # XXX: circular import
14
 
        from fields import RatingField
15
 
 
16
 
        qs = self.distinct().values_list(
17
 
            'content_type', 'object_id').order_by('content_type')
18
 
 
19
 
        to_update = []
20
 
        for content_type, objects in itertools.groupby(qs, key=lambda x: x[0]):
21
 
            model_class = ContentType.objects.get(
22
 
                pk=content_type).model_class()
23
 
            if model_class:
24
 
                to_update.extend(
25
 
                    list(model_class.objects.filter(pk__in=list(objects)[0])))
26
 
 
27
 
        retval = super(VoteQuerySet, self).delete(*args, **kwargs)
28
 
 
29
 
        # TODO: this could be improved
30
 
        for obj in to_update:
31
 
            for field in getattr(obj, '_djangoratings', []):
32
 
                getattr(obj, field.name)._update(commit=False)
33
 
            obj.save()
34
 
 
35
 
        return retval
36
 
 
37
 
 
38
 
class VoteManager(Manager):
39
 
 
40
 
    def get_query_set(self):
41
 
        return VoteQuerySet(self.model)
42
 
 
43
 
    def get_for_user_in_bulk(self, objects, user):
44
 
        objects = list(objects)
45
 
        if len(objects) > 0:
46
 
            ctype = ContentType.objects.get_for_model(objects[0])
47
 
            votes = list(self.filter(content_type__pk=ctype.id,
48
 
                                     object_id__in=[obj._get_pk_val()
49
 
                                                    for obj in objects],
50
 
                                     user__pk=user.id))
51
 
            vote_dict = dict([(vote.object_id, vote) for vote in votes])
52
 
        else:
53
 
            vote_dict = {}
54
 
        return vote_dict
55
 
 
56
 
 
57
 
class SimilarUserManager(Manager):
58
 
 
59
 
    def get_recommendations(self, user, model_class, min_score=1):
60
 
        from djangoratings.models import Vote, IgnoredObject
61
 
 
62
 
        content_type = ContentType.objects.get_for_model(model_class)
63
 
 
64
 
        params = dict(
65
 
            v=Vote._meta.db_table,
66
 
            sm=self.model._meta.db_table,
67
 
            m=model_class._meta.db_table,
68
 
            io=IgnoredObject._meta.db_table,
69
 
        )
70
 
 
71
 
        objects = model_class._default_manager.extra(
72
 
            tables=[params['v']],
73
 
            where=[
74
 
                '%(v)s.object_id = %(m)s.id and %(v)s.content_type_id = %%s' % params,
75
 
                '%(v)s.user_id IN (select to_user_id from %(sm)s where from_user_id = %%s and exclude = 0)' % params,
76
 
                '%(v)s.score >= %%s' % params,
77
 
                # Exclude already rated maps
78
 
                '%(v)s.object_id NOT IN (select object_id from %(v)s where content_type_id = %(v)s.content_type_id and user_id = %%s)' % params,
79
 
                # IgnoredObject exclusions
80
 
                '%(v)s.object_id NOT IN (select object_id from %(io)s where content_type_id = %(v)s.content_type_id and user_id = %%s)' % params,
81
 
            ],
82
 
            params=[content_type.id, user.id, min_score, user.id, user.id]
83
 
        ).distinct()
84
 
 
85
 
        # objects = model_class._default_manager.filter(pk__in=content_type.votes.extra(
86
 
        #     where=['user_id IN (select to_user_id from %s where from_user_id = %d and exclude = 0)' % (self.model._meta.db_table, user.pk)],
87
 
        # ).filter(score__gte=min_score).exclude(
88
 
        #     object_id__in=IgnoredObject.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True),
89
 
        # ).exclude(
90
 
        #     object_id__in=Vote.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True)
91
 
        # ).distinct().values_list('object_id', flat=True))
92
 
 
93
 
        return objects
94
 
 
95
 
    def update_recommendations(self):
96
 
        # TODO: this is mysql only atm
97
 
        # TODO: this doesnt handle scores that have multiple values (e.g. 10 points, 5 stars)
98
 
        # due to it calling an agreement as score = score. We need to loop each rating instance
99
 
        # and express the condition based on the range.
100
 
        from djangoratings.models import Vote
101
 
        from django.db import connection
102
 
        cursor = connection.cursor()
103
 
        cursor.execute('begin')
104
 
        cursor.execute('truncate table %s' % (self.model._meta.db_table,))
105
 
        cursor.execute("""insert into %(t1)s
106
 
          (to_user_id, from_user_id, agrees, disagrees, exclude)
107
 
          select v1.user_id, v2.user_id,
108
 
                 sum(if(v2.score = v1.score, 1, 0)) as agrees,
109
 
                 sum(if(v2.score != v1.score, 1, 0)) as disagrees, 0
110
 
            from %(t2)s as v1
111
 
              inner join %(t2)s as v2
112
 
                on v1.user_id != v2.user_id
113
 
                and v1.object_id = v2.object_id
114
 
                and v1.content_type_id = v2.content_type_id
115
 
            where v1.user_id is not null
116
 
              and v2.user_id is not null
117
 
            group by v1.user_id, v2.user_id
118
 
            having agrees / (disagrees + 0.0001) > 3
119
 
          on duplicate key update agrees = values(agrees), disagrees = values(disagrees);""" % dict(
120
 
            t1=self.model._meta.db_table,
121
 
            t2=Vote._meta.db_table,
122
 
        ))
123
 
        cursor.execute('commit')
124
 
        cursor.close()