~widelands-dev/widelands-website/trunk

« back to all changes in this revision

Viewing changes to djangoratings/fields.py

  • Committer: franku
  • Author(s): GunChleoc
  • Date: 2016-12-13 18:30:38 UTC
  • mfrom: (438.1.6 pyformat_util)
  • Revision ID: somal@arcor.de-20161213183038-5cgmvfh2fkgmoc1s
adding a script to run pyformat over the code base

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
from exceptions import *
11
11
 
12
12
if 'django.contrib.contenttypes' not in settings.INSTALLED_APPS:
13
 
    raise ImportError("djangoratings requires django.contrib.contenttypes in your INSTALLED_APPS")
 
13
    raise ImportError(
 
14
        'djangoratings requires django.contrib.contenttypes in your INSTALLED_APPS')
14
15
 
15
16
from django.contrib.contenttypes.models import ContentType
16
17
 
26
27
except ImportError:
27
28
    now = datetime.now
28
29
 
 
30
 
29
31
def md5_hexdigest(value):
30
32
    return md5(value).hexdigest()
31
33
 
 
34
 
32
35
class Rating(object):
 
36
 
33
37
    def __init__(self, score, votes):
34
38
        self.score = score
35
39
        self.votes = votes
36
40
 
 
41
 
37
42
class RatingManager(object):
 
43
 
38
44
    def __init__(self, instance, field):
39
45
        self.content_type = None
40
46
        self.instance = instance
41
47
        self.field = field
42
 
        
43
 
        self.votes_field_name = "%s_votes" % (self.field.name,)
44
 
        self.score_field_name = "%s_score" % (self.field.name,)
45
 
    
 
48
 
 
49
        self.votes_field_name = '%s_votes' % (self.field.name,)
 
50
        self.score_field_name = '%s_score' % (self.field.name,)
 
51
 
46
52
    def get_percent(self):
47
53
        """get_percent()
48
 
        
49
 
        Returns the weighted percentage of the score from min-max values"""
 
54
 
 
55
        Returns the weighted percentage of the score from min-max values
 
56
 
 
57
        """
50
58
        if not (self.votes and self.score):
51
59
            return 0
52
60
        return 100 * (self.get_rating() / self.field.range)
53
 
    
 
61
 
54
62
    def get_real_percent(self):
55
63
        """get_real_percent()
56
 
        
57
 
        Returns the unmodified percentage of the score based on a 0-point scale."""
 
64
 
 
65
        Returns the unmodified percentage of the score based on a 0-point scale.
 
66
 
 
67
        """
58
68
        if not (self.votes and self.score):
59
69
            return 0
60
70
        return 100 * (self.get_real_rating() / self.field.range)
61
 
    
 
71
 
62
72
    def get_ratings(self):
63
73
        """get_ratings()
64
 
        
65
 
        Returns a Vote QuerySet for this rating field."""
 
74
 
 
75
        Returns a Vote QuerySet for this rating field.
 
76
 
 
77
        """
66
78
        return Vote.objects.filter(content_type=self.get_content_type(), object_id=self.instance.pk, key=self.field.key)
67
 
        
 
79
 
68
80
    def get_rating(self):
69
81
        """get_rating()
70
 
        
71
 
        Returns the weighted average rating."""
 
82
 
 
83
        Returns the weighted average rating.
 
84
 
 
85
        """
72
86
        if not (self.votes and self.score):
73
87
            return 0
74
 
        return float(self.score)/(self.votes+self.field.weight)
75
 
    
 
88
        return float(self.score) / (self.votes + self.field.weight)
 
89
 
76
90
    def get_opinion_percent(self):
77
91
        """get_opinion_percent()
78
 
        
79
 
        Returns a neutral-based percentage."""
80
 
        return (self.get_percent()+100)/2
 
92
 
 
93
        Returns a neutral-based percentage.
 
94
 
 
95
        """
 
96
        return (self.get_percent() + 100) / 2
81
97
 
82
98
    def get_real_rating(self):
83
99
        """get_rating()
84
 
        
85
 
        Returns the unmodified average rating."""
 
100
 
 
101
        Returns the unmodified average rating.
 
102
 
 
103
        """
86
104
        if not (self.votes and self.score):
87
105
            return 0
88
 
        return float(self.score)/self.votes
89
 
    
 
106
        return float(self.score) / self.votes
 
107
 
90
108
    def get_rating_for_user(self, user, ip_address=None, cookies={}):
91
109
        """get_rating_for_user(user, ip_address=None, cookie=None)
92
 
        
 
110
 
93
111
        Returns the rating for a user or anonymous IP."""
94
112
        kwargs = dict(
95
 
            content_type    = self.get_content_type(),
96
 
            object_id       = self.instance.pk,
97
 
            key             = self.field.key,
 
113
            content_type=self.get_content_type(),
 
114
            object_id=self.instance.pk,
 
115
            key=self.field.key,
98
116
        )
99
117
 
100
118
        if not (user and user.is_authenticated()):
104
122
            kwargs['ip_address'] = ip_address
105
123
        else:
106
124
            kwargs['user'] = user
107
 
        
 
125
 
108
126
        use_cookies = (self.field.allow_anonymous and self.field.use_cookies)
109
127
        if use_cookies:
110
128
            # TODO: move 'vote-%d.%d.%s' to settings or something
111
 
            cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs['object_id'], kwargs['key'][:6],) # -> md5_hexdigest?
 
129
            cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[
 
130
                                             'object_id'], kwargs['key'][:6],)  # -> md5_hexdigest?
112
131
            cookie = cookies.get(cookie_name)
113
 
            if cookie:    
 
132
            if cookie:
114
133
                kwargs['cookie'] = cookie
115
134
            else:
116
135
                kwargs['cookie__isnull'] = True
117
 
            
 
136
 
118
137
        try:
119
138
            rating = Vote.objects.get(**kwargs)
120
139
            return rating.score
123
142
        except Vote.DoesNotExist:
124
143
            pass
125
144
        return
126
 
    
 
145
 
127
146
    def get_iterable_range(self):
128
 
        return range(1, self.field.range) #started from 1, because 0 is equal to delete
129
 
        
 
147
        # started from 1, because 0 is equal to delete
 
148
        return range(1, self.field.range)
 
149
 
130
150
    def add(self, score, user, ip_address, cookies={}, commit=True):
131
151
        """add(score, user, ip_address)
132
 
        
133
 
        Used to add a rating to an object."""
 
152
 
 
153
        Used to add a rating to an object.
 
154
 
 
155
        """
134
156
        try:
135
157
            score = int(score)
136
158
        except (ValueError, TypeError):
137
 
            raise InvalidRating("%s is not a valid choice for %s" % (score, self.field.name))
138
 
        
 
159
            raise InvalidRating('%s is not a valid choice for %s' %
 
160
                                (score, self.field.name))
 
161
 
139
162
        delete = (score == 0)
140
163
        if delete and not self.field.allow_delete:
141
 
            raise CannotDeleteVote("you are not allowed to delete votes for %s" % (self.field.name,))
 
164
            raise CannotDeleteVote(
 
165
                'you are not allowed to delete votes for %s' % (self.field.name,))
142
166
            # ... you're also can't delete your vote if you haven't permissions to change it. I leave this case for CannotChangeVote
143
 
        
 
167
 
144
168
        if score < 0 or score > self.field.range:
145
 
            raise InvalidRating("%s is not a valid choice for %s" % (score, self.field.name))
 
169
            raise InvalidRating('%s is not a valid choice for %s' %
 
170
                                (score, self.field.name))
146
171
 
147
172
        is_anonymous = (user is None or not user.is_authenticated())
148
173
        if is_anonymous and not self.field.allow_anonymous:
149
174
            raise AuthRequired("user must be a user, not '%r'" % (user,))
150
 
        
 
175
 
151
176
        if is_anonymous:
152
177
            user = None
153
 
        
 
178
 
154
179
        defaults = dict(
155
 
            score = score,
156
 
            ip_address = ip_address,
 
180
            score=score,
 
181
            ip_address=ip_address,
157
182
        )
158
 
        
 
183
 
159
184
        kwargs = dict(
160
 
            content_type    = self.get_content_type(),
161
 
            object_id       = self.instance.pk,
162
 
            key             = self.field.key,
163
 
            user            = user,
 
185
            content_type=self.get_content_type(),
 
186
            object_id=self.instance.pk,
 
187
            key=self.field.key,
 
188
            user=user,
164
189
        )
165
190
        if not user:
166
191
            kwargs['ip_address'] = ip_address
167
 
        
 
192
 
168
193
        use_cookies = (self.field.allow_anonymous and self.field.use_cookies)
169
194
        if use_cookies:
170
 
            defaults['cookie'] = now().strftime('%Y%m%d%H%M%S%f') # -> md5_hexdigest?
 
195
            defaults['cookie'] = now().strftime(
 
196
                '%Y%m%d%H%M%S%f')  # -> md5_hexdigest?
171
197
            # TODO: move 'vote-%d.%d.%s' to settings or something
172
 
            cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs['object_id'], kwargs['key'][:6],) # -> md5_hexdigest?
173
 
            cookie = cookies.get(cookie_name) # try to get existent cookie value
 
198
            cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[
 
199
                                             'object_id'], kwargs['key'][:6],)  # -> md5_hexdigest?
 
200
            # try to get existent cookie value
 
201
            cookie = cookies.get(cookie_name)
174
202
            if not cookie:
175
203
                kwargs['cookie__isnull'] = True
176
204
            kwargs['cookie'] = cookie
179
207
            rating, created = Vote.objects.get(**kwargs), False
180
208
        except Vote.DoesNotExist:
181
209
            if delete:
182
 
                raise CannotDeleteVote("attempt to find and delete your vote for %s is failed" % (self.field.name,))
 
210
                raise CannotDeleteVote(
 
211
                    'attempt to find and delete your vote for %s is failed' % (self.field.name,))
183
212
            if getattr(settings, 'RATINGS_VOTES_PER_IP', RATINGS_VOTES_PER_IP):
184
213
                num_votes = Vote.objects.filter(
185
214
                    content_type=kwargs['content_type'],
192
221
            kwargs.update(defaults)
193
222
            if use_cookies:
194
223
                # record with specified cookie was not found ...
195
 
                cookie = defaults['cookie'] # ... thus we need to replace old cookie (if presented) with new one
196
 
                kwargs.pop('cookie__isnull', '') # ... and remove 'cookie__isnull' (if presented) from .create()'s **kwargs
 
224
                # ... thus we need to replace old cookie (if presented) with new one
 
225
                cookie = defaults['cookie']
 
226
                # ... and remove 'cookie__isnull' (if presented) from .create()'s **kwargs
 
227
                kwargs.pop('cookie__isnull', '')
197
228
            rating, created = Vote.objects.create(**kwargs), True
198
 
            
 
229
 
199
230
        has_changed = False
200
231
        if not created:
201
232
            if self.field.can_change_vote:
202
233
                has_changed = True
203
234
                self.score -= rating.score
204
 
                # you can delete your vote only if you have permission to change your vote
 
235
                # you can delete your vote only if you have permission to
 
236
                # change your vote
205
237
                if not delete:
206
238
                    rating.score = score
207
239
                    rating.save()
219
251
            if commit:
220
252
                self.instance.save()
221
253
            #setattr(self.instance, self.field.name, Rating(score=self.score, votes=self.votes))
222
 
            
 
254
 
223
255
            defaults = dict(
224
 
                score   = self.score,
225
 
                votes   = self.votes,
 
256
                score=self.score,
 
257
                votes=self.votes,
226
258
            )
227
 
            
 
259
 
228
260
            kwargs = dict(
229
 
                content_type    = self.get_content_type(),
230
 
                object_id       = self.instance.pk,
231
 
                key             = self.field.key,
 
261
                content_type=self.get_content_type(),
 
262
                object_id=self.instance.pk,
 
263
                key=self.field.key,
232
264
            )
233
 
            
 
265
 
234
266
            try:
235
267
                score, created = Score.objects.get(**kwargs), False
236
268
            except Score.DoesNotExist:
237
269
                kwargs.update(defaults)
238
270
                score, created = Score.objects.create(**kwargs), True
239
 
            
 
271
 
240
272
            if not created:
241
273
                score.__dict__.update(defaults)
242
274
                score.save()
243
 
        
 
275
 
244
276
        # return value
245
277
        adds = {}
246
278
        if use_cookies:
252
284
 
253
285
    def delete(self, user, ip_address, cookies={}, commit=True):
254
286
        return self.add(0, user, ip_address, cookies, commit)
255
 
    
 
287
 
256
288
    def _get_votes(self, default=None):
257
289
        return getattr(self.instance, self.votes_field_name, default)
258
 
    
 
290
 
259
291
    def _set_votes(self, value):
260
292
        return setattr(self.instance, self.votes_field_name, value)
261
 
        
 
293
 
262
294
    votes = property(_get_votes, _set_votes)
263
295
 
264
296
    def _get_score(self, default=None):
265
297
        return getattr(self.instance, self.score_field_name, default)
266
 
    
 
298
 
267
299
    def _set_score(self, value):
268
300
        return setattr(self.instance, self.score_field_name, value)
269
 
        
 
301
 
270
302
    score = property(_get_score, _set_score)
271
303
 
272
304
    def get_content_type(self):
273
305
        if self.content_type is None:
274
 
            self.content_type = ContentType.objects.get_for_model(self.instance)
 
306
            self.content_type = ContentType.objects.get_for_model(
 
307
                self.instance)
275
308
        return self.content_type
276
 
    
 
309
 
277
310
    def _update(self, commit=False):
278
 
        """Forces an update of this rating (useful for when Vote objects are removed)."""
 
311
        """Forces an update of this rating (useful for when Vote objects are
 
312
        removed)."""
279
313
        votes = Vote.objects.filter(
280
 
            content_type    = self.get_content_type(),
281
 
            object_id       = self.instance.pk,
282
 
            key             = self.field.key,
 
314
            content_type=self.get_content_type(),
 
315
            object_id=self.instance.pk,
 
316
            key=self.field.key,
283
317
        )
284
318
        obj_score = sum([v.score for v in votes])
285
319
        obj_votes = len(votes)
286
320
 
287
321
        score, created = Score.objects.get_or_create(
288
 
            content_type    = self.get_content_type(),
289
 
            object_id       = self.instance.pk,
290
 
            key             = self.field.key,
291
 
            defaults        = dict(
292
 
                score       = obj_score,
293
 
                votes       = obj_votes,
 
322
            content_type=self.get_content_type(),
 
323
            object_id=self.instance.pk,
 
324
            key=self.field.key,
 
325
            defaults=dict(
 
326
                score=obj_score,
 
327
                votes=obj_votes,
294
328
            )
295
329
        )
296
330
        if not created:
302
336
        if commit:
303
337
            self.instance.save()
304
338
 
 
339
 
305
340
class RatingCreator(object):
 
341
 
306
342
    def __init__(self, field):
307
343
        self.field = field
308
 
        self.votes_field_name = "%s_votes" % (self.field.name,)
309
 
        self.score_field_name = "%s_score" % (self.field.name,)
 
344
        self.votes_field_name = '%s_votes' % (self.field.name,)
 
345
        self.score_field_name = '%s_score' % (self.field.name,)
310
346
 
311
347
    def __get__(self, instance, type=None):
312
348
        if instance is None:
319
355
            setattr(instance, self.votes_field_name, value.votes)
320
356
            setattr(instance, self.score_field_name, value.score)
321
357
        else:
322
 
            raise TypeError("%s value must be a Rating instance, not '%r'" % (self.field.name, value))
 
358
            raise TypeError("%s value must be a Rating instance, not '%r'" % (
 
359
                self.field.name, value))
 
360
 
323
361
 
324
362
class RatingField(IntegerField):
325
 
    """
326
 
    A rating field contributes two columns to the model instead of the standard single column.
327
 
    """
 
363
    """A rating field contributes two columns to the model instead of the
 
364
    standard single column."""
 
365
 
328
366
    def __init__(self, *args, **kwargs):
329
367
        if 'choices' in kwargs:
330
 
            raise TypeError("%s invalid attribute 'choices'" % (self.__class__.__name__,))
 
368
            raise TypeError("%s invalid attribute 'choices'" %
 
369
                            (self.__class__.__name__,))
331
370
        self.can_change_vote = kwargs.pop('can_change_vote', False)
332
371
        self.weight = kwargs.pop('weight', 0)
333
372
        self.range = kwargs.pop('range', 2)
338
377
        kwargs['default'] = 0
339
378
        kwargs['blank'] = True
340
379
        super(RatingField, self).__init__(*args, **kwargs)
341
 
    
 
380
 
342
381
    def contribute_to_class(self, cls, name):
343
382
        self.name = name
344
383
 
345
384
        # Votes tally field
346
385
        self.votes_field = PositiveIntegerField(
347
386
            editable=False, default=0, blank=True)
348
 
        cls.add_to_class("%s_votes" % (self.name,), self.votes_field)
 
387
        cls.add_to_class('%s_votes' % (self.name,), self.votes_field)
349
388
 
350
389
        # Score sum field
351
390
        self.score_field = IntegerField(
352
391
            editable=False, default=0, blank=True)
353
 
        cls.add_to_class("%s_score" % (self.name,), self.score_field)
 
392
        cls.add_to_class('%s_score' % (self.name,), self.score_field)
354
393
 
355
394
        self.key = md5_hexdigest(self.name)
356
395
 
371
410
        # TODO: order_by on this field should use the weighted algorithm
372
411
        raise NotImplementedError(self.get_db_prep_lookup)
373
412
        # if lookup_type in ('score', 'votes'):
374
 
        #     lookup_type = 
 
413
        #     lookup_type =
375
414
        #     return self.score_field.get_db_prep_lookup()
376
415
        if lookup_type == 'exact':
377
416
            return [self.get_db_prep_save(value)]
389
428
 
390
429
 
391
430
class AnonymousRatingField(RatingField):
 
431
 
392
432
    def __init__(self, *args, **kwargs):
393
433
        kwargs['allow_anonymous'] = True
394
434
        super(AnonymousRatingField, self).__init__(*args, **kwargs)