~widelands-dev/widelands-website/django_staticfiles

« back to all changes in this revision

Viewing changes to djangoratings/managers.py

  • Committer: franku
  • Date: 2018-10-03 09:01:09 UTC
  • mto: This revision was merged to the branch mainline in revision 502.
  • Revision ID: somal@arcor.de-20181003090109-so4zn8x9vujarq6n
removed IPAddressField from pybb

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()