widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #15356
[Merge] lp:~widelands-dev/widelands-website/remove_djangoratings into lp:widelands-website
kaputtnik has proposed merging lp:~widelands-dev/widelands-website/remove_djangoratings into lp:widelands-website.
Commit message:
Remove djangoratings
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #1762158 in Widelands Website: "Replace django-ratings with an up to date app"
https://bugs.launchpad.net/widelands-website/+bug/1762158
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands-website/remove_djangoratings/+merge/358960
Follow up of https://code.launchpad.net/~widelands-dev/widelands-website/replace_djangoratings
Bye, bye djangoratings :-D
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands-website/remove_djangoratings into lp:widelands-website.
=== removed directory 'djangoratings'
=== removed file 'djangoratings/LICENSE'
--- djangoratings/LICENSE 2016-05-18 19:31:46 +0000
+++ djangoratings/LICENSE 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
-Copyright (c) 2009, David Cramer <dcramer@xxxxxxxxx>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=== removed file 'djangoratings/__init__.py'
--- djangoratings/__init__.py 2016-12-13 18:28:51 +0000
+++ djangoratings/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
-import os.path
-import warnings
-
-__version__ = (0, 3, 7)
-
-
-def _get_git_revision(path):
- revision_file = os.path.join(path, 'refs', 'heads', 'master')
- if not os.path.exists(revision_file):
- return None
- fh = open(revision_file, 'r')
- try:
- return fh.read()
- finally:
- fh.close()
-
-
-def get_revision():
- """
- :returns: Revision number of this branch/checkout, if available. None if
- no revision number can be determined.
- """
- package_dir = os.path.dirname(__file__)
- checkout_dir = os.path.normpath(os.path.join(package_dir, '..'))
- path = os.path.join(checkout_dir, '.git')
- if os.path.exists(path):
- return _get_git_revision(path)
- return None
-
-__build__ = get_revision()
-
-
-def lazy_object(location):
- def inner(*args, **kwargs):
- parts = location.rsplit('.', 1)
- warnings.warn('`djangoratings.%s` is deprecated. Please use `%s` instead.' % (
- parts[1], location), DeprecationWarning)
- try:
- imp = __import__(parts[0], globals(), locals(), [parts[1]], -1)
- except:
- imp = __import__(parts[0], globals(), locals(), [parts[1]])
- func = getattr(imp, parts[1])
- if callable(func):
- return func(*args, **kwargs)
- return func
- return inner
-
-RatingField = lazy_object('djangoratings.fields.RatingField')
-AnonymousRatingField = lazy_object('djangoratings.fields.AnonymousRatingField')
-Rating = lazy_object('djangoratings.fields.Rating')
=== removed file 'djangoratings/admin.py'
--- djangoratings/admin.py 2016-12-13 18:28:51 +0000
+++ djangoratings/admin.py 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
-from django.contrib import admin
-from models import Vote, Score
-
-
-class VoteAdmin(admin.ModelAdmin):
- list_display = ('content_object', 'user', 'ip_address',
- 'cookie', 'score', 'date_changed')
- list_filter = ('score', 'content_type', 'date_changed')
- search_fields = ('ip_address',)
- raw_id_fields = ('user',)
-
-
-class ScoreAdmin(admin.ModelAdmin):
- list_display = ('content_object', 'score', 'votes')
- list_filter = ('content_type',)
-
-admin.site.register(Vote, VoteAdmin)
-admin.site.register(Score, ScoreAdmin)
=== removed file 'djangoratings/default_settings.py'
--- djangoratings/default_settings.py 2016-12-13 18:28:51 +0000
+++ djangoratings/default_settings.py 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-from django.conf import settings
-
-# Used to limit the number of unique IPs that can vote on a single object+field.
-# useful if you're getting rating spam by users registering multiple accounts
-RATINGS_VOTES_PER_IP = 3
=== removed file 'djangoratings/exceptions.py'
--- djangoratings/exceptions.py 2016-12-13 18:28:51 +0000
+++ djangoratings/exceptions.py 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
-class InvalidRating(ValueError):
- pass
-
-
-class AuthRequired(TypeError):
- pass
-
-
-class CannotChangeVote(Exception):
- pass
-
-
-class CannotDeleteVote(Exception):
- pass
-
-
-class IPLimitReached(Exception):
- pass
=== removed file 'djangoratings/fields.py'
--- djangoratings/fields.py 2018-04-08 14:29:44 +0000
+++ djangoratings/fields.py 1970-01-01 00:00:00 +0000
@@ -1,434 +0,0 @@
-from django.db.models import IntegerField, PositiveIntegerField
-from django.conf import settings
-
-import forms
-import itertools
-from datetime import datetime
-
-from models import Vote, Score
-from default_settings import RATINGS_VOTES_PER_IP
-from exceptions import *
-
-if 'django.contrib.contenttypes' not in settings.INSTALLED_APPS:
- raise ImportError(
- 'djangoratings requires django.contrib.contenttypes in your INSTALLED_APPS')
-
-from django.contrib.contenttypes.models import ContentType
-
-__all__ = ('Rating', 'RatingField', 'AnonymousRatingField')
-
-try:
- from hashlib import md5
-except ImportError:
- from md5 import new as md5
-
-try:
- from django.utils.timezone import now
-except ImportError:
- now = datetime.now
-
-
-def md5_hexdigest(value):
- return md5(value).hexdigest()
-
-
-class Rating(object):
-
- def __init__(self, score, votes):
- self.score = score
- self.votes = votes
-
-
-class RatingManager(object):
-
- def __init__(self, instance, field):
- self.content_type = None
- self.instance = instance
- self.field = field
-
- self.votes_field_name = '%s_votes' % (self.field.name,)
- self.score_field_name = '%s_score' % (self.field.name,)
-
- def get_percent(self):
- """get_percent()
-
- Returns the weighted percentage of the score from min-max values
-
- """
- if not (self.votes and self.score):
- return 0
- return 100 * (self.get_rating() / self.field.range)
-
- def get_real_percent(self):
- """get_real_percent()
-
- Returns the unmodified percentage of the score based on a 0-point scale.
-
- """
- if not (self.votes and self.score):
- return 0
- return 100 * (self.get_real_rating() / self.field.range)
-
- def get_ratings(self):
- """get_ratings()
-
- Returns a Vote QuerySet for this rating field.
-
- """
- return Vote.objects.filter(content_type=self.get_content_type(), object_id=self.instance.pk, key=self.field.key)
-
- def get_rating(self):
- """get_rating()
-
- Returns the weighted average rating.
-
- """
- if not (self.votes and self.score):
- return 0
- return float(self.score) / (self.votes + self.field.weight)
-
- def get_opinion_percent(self):
- """get_opinion_percent()
-
- Returns a neutral-based percentage.
-
- """
- return (self.get_percent() + 100) / 2
-
- def get_real_rating(self):
- """get_rating()
-
- Returns the unmodified average rating.
-
- """
- if not (self.votes and self.score):
- return 0
- return float(self.score) / self.votes
-
- def get_rating_for_user(self, user, ip_address=None, cookies={}):
- """get_rating_for_user(user, ip_address=None, cookie=None)
-
- Returns the rating for a user or anonymous IP."""
- kwargs = dict(
- content_type=self.get_content_type(),
- object_id=self.instance.pk,
- key=self.field.key,
- )
-
- if not (user and user.is_authenticated):
- if not ip_address:
- raise ValueError('``user`` or ``ip_address`` must be present.')
- kwargs['user__isnull'] = True
- kwargs['ip_address'] = ip_address
- else:
- kwargs['user'] = user
-
- use_cookies = (self.field.allow_anonymous and self.field.use_cookies)
- if use_cookies:
- # TODO: move 'vote-%d.%d.%s' to settings or something
- cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[
- 'object_id'], kwargs['key'][:6],) # -> md5_hexdigest?
- cookie = cookies.get(cookie_name)
- if cookie:
- kwargs['cookie'] = cookie
- else:
- kwargs['cookie__isnull'] = True
-
- try:
- rating = Vote.objects.get(**kwargs)
- return rating.score
- except Vote.MultipleObjectsReturned:
- pass
- except Vote.DoesNotExist:
- pass
- return
-
- def get_iterable_range(self):
- # started from 1, because 0 is equal to delete
- return range(1, self.field.range)
-
- def add(self, score, user, ip_address, cookies={}, commit=True):
- """add(score, user, ip_address)
-
- Used to add a rating to an object.
-
- """
- try:
- score = int(score)
- except (ValueError, TypeError):
- raise InvalidRating('%s is not a valid choice for %s' %
- (score, self.field.name))
-
- delete = (score == 0)
- if delete and not self.field.allow_delete:
- raise CannotDeleteVote(
- 'you are not allowed to delete votes for %s' % (self.field.name,))
- # ... you're also can't delete your vote if you haven't permissions to change it. I leave this case for CannotChangeVote
-
- if score < 0 or score > self.field.range:
- raise InvalidRating('%s is not a valid choice for %s' %
- (score, self.field.name))
-
- is_anonymous = (user is None or not user.is_authenticated)
- if is_anonymous and not self.field.allow_anonymous:
- raise AuthRequired("user must be a user, not '%r'" % (user,))
-
- if is_anonymous:
- user = None
-
- defaults = dict(
- score=score,
- ip_address=ip_address,
- )
-
- kwargs = dict(
- content_type=self.get_content_type(),
- object_id=self.instance.pk,
- key=self.field.key,
- user=user,
- )
- if not user:
- kwargs['ip_address'] = ip_address
-
- use_cookies = (self.field.allow_anonymous and self.field.use_cookies)
- if use_cookies:
- defaults['cookie'] = now().strftime(
- '%Y%m%d%H%M%S%f') # -> md5_hexdigest?
- # TODO: move 'vote-%d.%d.%s' to settings or something
- cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[
- 'object_id'], kwargs['key'][:6],) # -> md5_hexdigest?
- # try to get existent cookie value
- cookie = cookies.get(cookie_name)
- if not cookie:
- kwargs['cookie__isnull'] = True
- kwargs['cookie'] = cookie
-
- try:
- rating, created = Vote.objects.get(**kwargs), False
- except Vote.DoesNotExist:
- if delete:
- raise CannotDeleteVote(
- 'attempt to find and delete your vote for %s is failed' % (self.field.name,))
- if getattr(settings, 'RATINGS_VOTES_PER_IP', RATINGS_VOTES_PER_IP):
- num_votes = Vote.objects.filter(
- content_type=kwargs['content_type'],
- object_id=kwargs['object_id'],
- key=kwargs['key'],
- ip_address=ip_address,
- ).count()
- if num_votes >= getattr(settings, 'RATINGS_VOTES_PER_IP', RATINGS_VOTES_PER_IP):
- raise IPLimitReached()
- kwargs.update(defaults)
- if use_cookies:
- # record with specified cookie was not found ...
- # ... thus we need to replace old cookie (if presented) with new one
- cookie = defaults['cookie']
- # ... and remove 'cookie__isnull' (if presented) from .create()'s **kwargs
- kwargs.pop('cookie__isnull', '')
- rating, created = Vote.objects.create(**kwargs), True
-
- has_changed = False
- if not created:
- if self.field.can_change_vote:
- has_changed = True
- self.score -= rating.score
- # you can delete your vote only if you have permission to
- # change your vote
- if not delete:
- rating.score = score
- rating.save()
- else:
- self.votes -= 1
- rating.delete()
- else:
- raise CannotChangeVote()
- else:
- has_changed = True
- self.votes += 1
- if has_changed:
- if not delete:
- self.score += rating.score
- if commit:
- self.instance.save()
- #setattr(self.instance, self.field.name, Rating(score=self.score, votes=self.votes))
-
- defaults = dict(
- score=self.score,
- votes=self.votes,
- )
-
- kwargs = dict(
- content_type=self.get_content_type(),
- object_id=self.instance.pk,
- key=self.field.key,
- )
-
- try:
- score, created = Score.objects.get(**kwargs), False
- except Score.DoesNotExist:
- kwargs.update(defaults)
- score, created = Score.objects.create(**kwargs), True
-
- if not created:
- score.__dict__.update(defaults)
- score.save()
-
- # return value
- adds = {}
- if use_cookies:
- adds['cookie_name'] = cookie_name
- adds['cookie'] = cookie
- if delete:
- adds['deleted'] = True
- return adds
-
- def delete(self, user, ip_address, cookies={}, commit=True):
- return self.add(0, user, ip_address, cookies, commit)
-
- def _get_votes(self, default=None):
- return getattr(self.instance, self.votes_field_name, default)
-
- def _set_votes(self, value):
- return setattr(self.instance, self.votes_field_name, value)
-
- votes = property(_get_votes, _set_votes)
-
- def _get_score(self, default=None):
- return getattr(self.instance, self.score_field_name, default)
-
- def _set_score(self, value):
- return setattr(self.instance, self.score_field_name, value)
-
- score = property(_get_score, _set_score)
-
- def get_content_type(self):
- if self.content_type is None:
- self.content_type = ContentType.objects.get_for_model(
- self.instance)
- return self.content_type
-
- def _update(self, commit=False):
- """Forces an update of this rating (useful for when Vote objects are
- removed)."""
- votes = Vote.objects.filter(
- content_type=self.get_content_type(),
- object_id=self.instance.pk,
- key=self.field.key,
- )
- obj_score = sum([v.score for v in votes])
- obj_votes = len(votes)
-
- score, created = Score.objects.get_or_create(
- content_type=self.get_content_type(),
- object_id=self.instance.pk,
- key=self.field.key,
- defaults=dict(
- score=obj_score,
- votes=obj_votes,
- )
- )
- if not created:
- score.score = obj_score
- score.votes = obj_votes
- score.save()
- self.score = obj_score
- self.votes = obj_votes
- if commit:
- self.instance.save()
-
-
-class RatingCreator(object):
-
- def __init__(self, field):
- self.field = field
- self.votes_field_name = '%s_votes' % (self.field.name,)
- self.score_field_name = '%s_score' % (self.field.name,)
-
- def __get__(self, instance, type=None):
- if instance is None:
- return self.field
- #raise AttributeError('Can only be accessed via an instance.')
- return RatingManager(instance, self.field)
-
- def __set__(self, instance, value):
- if isinstance(value, Rating):
- setattr(instance, self.votes_field_name, value.votes)
- setattr(instance, self.score_field_name, value.score)
- else:
- raise TypeError("%s value must be a Rating instance, not '%r'" % (
- self.field.name, value))
-
-
-class RatingField(IntegerField):
- """A rating field contributes two columns to the model instead of the
- standard single column."""
-
- def __init__(self, *args, **kwargs):
- if 'choices' in kwargs:
- raise TypeError("%s invalid attribute 'choices'" %
- (self.__class__.__name__,))
- self.can_change_vote = kwargs.pop('can_change_vote', False)
- self.weight = kwargs.pop('weight', 0)
- self.range = kwargs.pop('range', 2)
- self.allow_anonymous = kwargs.pop('allow_anonymous', False)
- self.use_cookies = kwargs.pop('use_cookies', False)
- self.allow_delete = kwargs.pop('allow_delete', False)
- kwargs['editable'] = False
- kwargs['default'] = 0
- kwargs['blank'] = True
- super(RatingField, self).__init__(*args, **kwargs)
-
- def contribute_to_class(self, cls, name):
- self.name = name
-
- # Votes tally field
- self.votes_field = PositiveIntegerField(
- editable=False, default=0, blank=True)
- cls.add_to_class('%s_votes' % (self.name,), self.votes_field)
-
- # Score sum field
- self.score_field = IntegerField(
- editable=False, default=0, blank=True)
- cls.add_to_class('%s_score' % (self.name,), self.score_field)
-
- self.key = md5_hexdigest(self.name)
-
- field = RatingCreator(self)
-
- if not hasattr(cls, '_djangoratings'):
- cls._djangoratings = []
- cls._djangoratings.append(self)
-
- setattr(cls, name, field)
-
- def get_db_prep_save(self, value):
- # XXX: what happens here?
- pass
-
- def get_db_prep_lookup(self, lookup_type, value):
- # TODO: hack in support for __score and __votes
- # TODO: order_by on this field should use the weighted algorithm
- raise NotImplementedError(self.get_db_prep_lookup)
- # if lookup_type in ('score', 'votes'):
- # lookup_type =
- # return self.score_field.get_db_prep_lookup()
- if lookup_type == 'exact':
- return [self.get_db_prep_save(value)]
- elif lookup_type == 'in':
- return [self.get_db_prep_save(v) for v in value]
- else:
- return super(RatingField, self).get_db_prep_lookup(lookup_type, value)
-
- def formfield(self, **kwargs):
- defaults = {'form_class': forms.RatingField}
- defaults.update(kwargs)
- return super(RatingField, self).formfield(**defaults)
-
- # TODO: flatten_data method
-
-
-class AnonymousRatingField(RatingField):
-
- def __init__(self, *args, **kwargs):
- kwargs['allow_anonymous'] = True
- super(AnonymousRatingField, self).__init__(*args, **kwargs)
=== removed file 'djangoratings/forms.py'
--- djangoratings/forms.py 2016-12-13 18:28:51 +0000
+++ djangoratings/forms.py 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-from django import forms
-
-__all__ = ('RatingField',)
-
-
-class RatingField(forms.ChoiceField):
- pass
=== removed directory 'djangoratings/management'
=== removed file 'djangoratings/management/__init__.py'
=== removed directory 'djangoratings/management/commands'
=== removed file 'djangoratings/management/commands/__init__.py'
=== removed file 'djangoratings/management/commands/update_recommendations.py'
--- djangoratings/management/commands/update_recommendations.py 2018-04-05 07:30:42 +0000
+++ djangoratings/management/commands/update_recommendations.py 1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
-from django.core.management.base import BaseCommand, CommandError
-
-from djangoratings.models import SimilarUser
-
-
-class Command(BaseCommand):
-
- def handle(self, *args, **options):
- SimilarUser.objects.update_recommendations()
=== removed file 'djangoratings/managers.py'
--- djangoratings/managers.py 2016-12-13 18:28:51 +0000
+++ djangoratings/managers.py 1970-01-01 00:00:00 +0000
@@ -1,124 +0,0 @@
-from django.db.models import Manager
-from django.db.models.query import QuerySet
-
-from django.contrib.contenttypes.models import ContentType
-import itertools
-
-
-class VoteQuerySet(QuerySet):
-
- def delete(self, *args, **kwargs):
- """Handles updating the related `votes` and `score` fields attached to
- the model."""
- # XXX: circular import
- from fields import RatingField
-
- qs = self.distinct().values_list(
- 'content_type', 'object_id').order_by('content_type')
-
- to_update = []
- for content_type, objects in itertools.groupby(qs, key=lambda x: x[0]):
- model_class = ContentType.objects.get(
- pk=content_type).model_class()
- if model_class:
- to_update.extend(
- list(model_class.objects.filter(pk__in=list(objects)[0])))
-
- retval = super(VoteQuerySet, self).delete(*args, **kwargs)
-
- # TODO: this could be improved
- for obj in to_update:
- for field in getattr(obj, '_djangoratings', []):
- getattr(obj, field.name)._update(commit=False)
- obj.save()
-
- return retval
-
-
-class VoteManager(Manager):
-
- def get_query_set(self):
- return VoteQuerySet(self.model)
-
- def get_for_user_in_bulk(self, objects, user):
- objects = list(objects)
- if len(objects) > 0:
- ctype = ContentType.objects.get_for_model(objects[0])
- votes = list(self.filter(content_type__pk=ctype.id,
- object_id__in=[obj._get_pk_val()
- for obj in objects],
- user__pk=user.id))
- vote_dict = dict([(vote.object_id, vote) for vote in votes])
- else:
- vote_dict = {}
- return vote_dict
-
-
-class SimilarUserManager(Manager):
-
- def get_recommendations(self, user, model_class, min_score=1):
- from djangoratings.models import Vote, IgnoredObject
-
- content_type = ContentType.objects.get_for_model(model_class)
-
- params = dict(
- v=Vote._meta.db_table,
- sm=self.model._meta.db_table,
- m=model_class._meta.db_table,
- io=IgnoredObject._meta.db_table,
- )
-
- objects = model_class._default_manager.extra(
- tables=[params['v']],
- where=[
- '%(v)s.object_id = %(m)s.id and %(v)s.content_type_id = %%s' % params,
- '%(v)s.user_id IN (select to_user_id from %(sm)s where from_user_id = %%s and exclude = 0)' % params,
- '%(v)s.score >= %%s' % params,
- # Exclude already rated maps
- '%(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,
- # IgnoredObject exclusions
- '%(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,
- ],
- params=[content_type.id, user.id, min_score, user.id, user.id]
- ).distinct()
-
- # objects = model_class._default_manager.filter(pk__in=content_type.votes.extra(
- # 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)],
- # ).filter(score__gte=min_score).exclude(
- # object_id__in=IgnoredObject.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True),
- # ).exclude(
- # object_id__in=Vote.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True)
- # ).distinct().values_list('object_id', flat=True))
-
- return objects
-
- def update_recommendations(self):
- # TODO: this is mysql only atm
- # TODO: this doesnt handle scores that have multiple values (e.g. 10 points, 5 stars)
- # due to it calling an agreement as score = score. We need to loop each rating instance
- # and express the condition based on the range.
- from djangoratings.models import Vote
- from django.db import connection
- cursor = connection.cursor()
- cursor.execute('begin')
- cursor.execute('truncate table %s' % (self.model._meta.db_table,))
- cursor.execute("""insert into %(t1)s
- (to_user_id, from_user_id, agrees, disagrees, exclude)
- select v1.user_id, v2.user_id,
- sum(if(v2.score = v1.score, 1, 0)) as agrees,
- sum(if(v2.score != v1.score, 1, 0)) as disagrees, 0
- from %(t2)s as v1
- inner join %(t2)s as v2
- on v1.user_id != v2.user_id
- and v1.object_id = v2.object_id
- and v1.content_type_id = v2.content_type_id
- where v1.user_id is not null
- and v2.user_id is not null
- group by v1.user_id, v2.user_id
- having agrees / (disagrees + 0.0001) > 3
- on duplicate key update agrees = values(agrees), disagrees = values(disagrees);""" % dict(
- t1=self.model._meta.db_table,
- t2=Vote._meta.db_table,
- ))
- cursor.execute('commit')
- cursor.close()
=== removed directory 'djangoratings/migrations'
=== removed file 'djangoratings/migrations/0001_initial.py'
--- djangoratings/migrations/0001_initial.py 2016-12-13 18:28:51 +0000
+++ djangoratings/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import models, migrations
-import django.utils.timezone
-from django.conf import settings
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('contenttypes', '0002_remove_content_type_name'),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='IgnoredObject',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('object_id', models.PositiveIntegerField()),
- ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
- ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.CreateModel(
- name='Score',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('object_id', models.PositiveIntegerField()),
- ('key', models.CharField(max_length=32)),
- ('score', models.IntegerField()),
- ('votes', models.PositiveIntegerField()),
- ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
- ],
- ),
- migrations.CreateModel(
- name='SimilarUser',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('agrees', models.PositiveIntegerField(default=0)),
- ('disagrees', models.PositiveIntegerField(default=0)),
- ('exclude', models.BooleanField(default=False)),
- ('from_user', models.ForeignKey(
- related_name='similar_users', to=settings.AUTH_USER_MODEL)),
- ('to_user', models.ForeignKey(
- related_name='similar_users_from', to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.CreateModel(
- name='Vote',
- fields=[
- ('id', models.AutoField(verbose_name='ID',
- serialize=False, auto_created=True, primary_key=True)),
- ('object_id', models.PositiveIntegerField()),
- ('key', models.CharField(max_length=32)),
- ('score', models.IntegerField()),
- ('ip_address', models.GenericIPAddressField()),
- ('cookie', models.CharField(max_length=32, null=True, blank=True)),
- ('date_added', models.DateTimeField(
- default=django.utils.timezone.now, editable=False)),
- ('date_changed', models.DateTimeField(
- default=django.utils.timezone.now, editable=False)),
- ('content_type', models.ForeignKey(
- related_name='votes', to='contenttypes.ContentType')),
- ('user', models.ForeignKey(related_name='votes',
- blank=True, to=settings.AUTH_USER_MODEL, null=True)),
- ],
- ),
- migrations.AlterUniqueTogether(
- name='vote',
- unique_together=set(
- [('content_type', 'object_id', 'key', 'user', 'ip_address', 'cookie')]),
- ),
- migrations.AlterUniqueTogether(
- name='similaruser',
- unique_together=set([('from_user', 'to_user')]),
- ),
- migrations.AlterUniqueTogether(
- name='score',
- unique_together=set([('content_type', 'object_id', 'key')]),
- ),
- migrations.AlterUniqueTogether(
- name='ignoredobject',
- unique_together=set([('content_type', 'object_id')]),
- ),
- ]
=== removed file 'djangoratings/migrations/__init__.py'
=== removed file 'djangoratings/models.py'
--- djangoratings/models.py 2016-12-13 18:28:51 +0000
+++ djangoratings/models.py 1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
-from datetime import datetime
-
-from django.db import models
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes.fields import GenericForeignKey
-from django.contrib.auth.models import User
-
-try:
- from django.utils.timezone import now
-except ImportError:
- now = datetime.now
-
-from managers import VoteManager, SimilarUserManager
-
-
-class Vote(models.Model):
- content_type = models.ForeignKey(ContentType, related_name='votes')
- object_id = models.PositiveIntegerField()
- key = models.CharField(max_length=32)
- score = models.IntegerField()
- user = models.ForeignKey(User, blank=True, null=True, related_name='votes')
- ip_address = models.GenericIPAddressField()
- cookie = models.CharField(max_length=32, blank=True, null=True)
- date_added = models.DateTimeField(default=now, editable=False)
- date_changed = models.DateTimeField(default=now, editable=False)
-
- objects = VoteManager()
-
- content_object = GenericForeignKey()
-
- class Meta:
- unique_together = (('content_type', 'object_id',
- 'key', 'user', 'ip_address', 'cookie'))
-
- def __unicode__(self):
- return u"%s voted %s on %s" % (self.user_display, self.score, self.content_object)
-
- def save(self, *args, **kwargs):
- self.date_changed = now()
- super(Vote, self).save(*args, **kwargs)
-
- def user_display(self):
- if self.user:
- return '%s (%s)' % (self.user.username, self.ip_address)
- return self.ip_address
- user_display = property(user_display)
-
- def partial_ip_address(self):
- ip = self.ip_address.split('.')
- ip[-1] = 'xxx'
- return '.'.join(ip)
- partial_ip_address = property(partial_ip_address)
-
-
-class Score(models.Model):
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- key = models.CharField(max_length=32)
- score = models.IntegerField()
- votes = models.PositiveIntegerField()
-
- content_object = GenericForeignKey()
-
- class Meta:
- unique_together = (('content_type', 'object_id', 'key'),)
-
- def __unicode__(self):
- return u"%s scored %s with %s votes" % (self.content_object, self.score, self.votes)
-
-
-class SimilarUser(models.Model):
- from_user = models.ForeignKey(User, related_name='similar_users')
- to_user = models.ForeignKey(User, related_name='similar_users_from')
- agrees = models.PositiveIntegerField(default=0)
- disagrees = models.PositiveIntegerField(default=0)
- exclude = models.BooleanField(default=False)
-
- objects = SimilarUserManager()
-
- class Meta:
- unique_together = (('from_user', 'to_user'),)
-
- def __unicode__(self):
- print u"%s %s similar to %s" % (self.from_user, self.exclude and 'is not' or 'is', self.to_user)
-
-
-class IgnoredObject(models.Model):
- user = models.ForeignKey(User)
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
-
- content_object = GenericForeignKey()
-
- class Meta:
- unique_together = (('content_type', 'object_id'),)
-
- def __unicode__(self):
- return self.content_object
=== removed file 'djangoratings/runtests.py'
--- djangoratings/runtests.py 2016-12-13 18:28:51 +0000
+++ djangoratings/runtests.py 1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-import sys
-
-from os.path import dirname, abspath
-
-from django.conf import settings
-
-if not settings.configured:
- settings.configure(
- DATABASE_ENGINE='sqlite3',
- INSTALLED_APPS=[
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'djangoratings',
- ]
- )
-
-from django.test.simple import run_tests
-
-
-def runtests(*test_args):
- if not test_args:
- test_args = ['djangoratings']
- parent = dirname(abspath(__file__))
- sys.path.insert(0, parent)
- failures = run_tests(test_args, verbosity=1, interactive=True)
- sys.exit(failures)
-
-
-if __name__ == '__main__':
- runtests(*sys.argv[1:])
=== removed directory 'djangoratings/templatetags'
=== removed file 'djangoratings/templatetags/__init__.py'
=== removed file 'djangoratings/templatetags/ratings.py'
--- djangoratings/templatetags/ratings.py 2018-04-05 07:30:42 +0000
+++ djangoratings/templatetags/ratings.py 1970-01-01 00:00:00 +0000
@@ -1,101 +0,0 @@
-"""Template tags for Django."""
-# TODO: add in Jinja tags if Coffin is available
-
-from django import template
-from django.contrib.contenttypes.models import ContentType
-from django.db.models import ObjectDoesNotExist
-
-from djangoratings.models import Vote
-from wl_utils import get_real_ip
-
-register = template.Library()
-
-
-class RatingByRequestNode(template.Node):
-
- def __init__(self, request, obj, context_var):
- self.request = request
- self.obj, self.field_name = obj.split('.')
- self.context_var = context_var
-
- def render(self, context):
- try:
- request = django.template.Variable(self.request).resolve(context)
- obj = django.template.Variable(self.obj).resolve(context)
- field = getattr(obj, self.field_name)
- except (template.VariableDoesNotExist, AttributeError):
- return ''
- try:
- vote = field.get_rating_for_user(
- request.user, get_real_ip(request), request.COOKIES)
- context[self.context_var] = vote
- except ObjectDoesNotExist:
- context[self.context_var] = 0
- return ''
-
-
-def do_rating_by_request(parser, token):
- """
- Retrieves the ``Vote`` cast by a user on a particular object and
- stores it in a context variable. If the user has not voted, the
- context variable will be 0.
-
- Example usage::
-
- {% rating_by_request request on instance as vote %}
- """
-
- bits = token.contents.split()
- if len(bits) != 6:
- raise template.TemplateSyntaxError(
- "'%s' tag takes exactly five arguments" % bits[0])
- if bits[2] != 'on':
- raise template.TemplateSyntaxError(
- "second argument to '%s' tag must be 'on'" % bits[0])
- if bits[4] != 'as':
- raise template.TemplateSyntaxError(
- "fourth argument to '%s' tag must be 'as'" % bits[0])
- return RatingByRequestNode(bits[1], bits[3], bits[5])
-register.tag('rating_by_request', do_rating_by_request)
-
-
-class RatingByUserNode(RatingByRequestNode):
-
- def render(self, context):
- try:
- user = django.template.Variable(self.request).resolve(context)
- obj = django.template.Variable(self.obj).resolve(context)
- field = getattr(obj, self.field_name)
- except template.VariableDoesNotExist:
- return ''
- try:
- vote = field.get_rating_for_user(user)
- context[self.context_var] = vote
- except ObjectDoesNotExist:
- context[self.context_var] = 0
- return ''
-
-
-def do_rating_by_user(parser, token):
- """
- Retrieves the ``Vote`` cast by a user on a particular object and
- stores it in a context variable. If the user has not voted, the
- context variable will be 0.
-
- Example usage::
-
- {% rating_by_user user on instance as vote %}
- """
-
- bits = token.contents.split()
- if len(bits) != 6:
- raise template.TemplateSyntaxError(
- "'%s' tag takes exactly five arguments" % bits[0])
- if bits[2] != 'on':
- raise template.TemplateSyntaxError(
- "second argument to '%s' tag must be 'on'" % bits[0])
- if bits[4] != 'as':
- raise template.TemplateSyntaxError(
- "fourth argument to '%s' tag must be 'as'" % bits[0])
- return RatingByUserNode(bits[1], bits[3], bits[5])
-register.tag('rating_by_user', do_rating_by_user)
=== removed file 'djangoratings/tests.py'
--- djangoratings/tests.py 2016-12-13 18:28:51 +0000
+++ djangoratings/tests.py 1970-01-01 00:00:00 +0000
@@ -1,184 +0,0 @@
-import unittest
-import random
-
-from django.db import models
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.conf import settings
-
-from exceptions import *
-from models import Vote, SimilarUser, IgnoredObject
-from fields import AnonymousRatingField, RatingField
-
-settings.RATINGS_VOTES_PER_IP = 1
-
-
-class RatingTestModel(models.Model):
- rating = AnonymousRatingField(range=2, can_change_vote=True)
- rating2 = RatingField(range=2, can_change_vote=False)
-
- def __unicode__(self):
- return unicode(self.pk)
-
-
-class RatingTestCase(unittest.TestCase):
-
- def testRatings(self):
- instance = RatingTestModel.objects.create()
-
- # Test adding votes
- instance.rating.add(score=1, user=None, ip_address='127.0.0.1')
- self.assertEquals(instance.rating.score, 1)
- self.assertEquals(instance.rating.votes, 1)
-
- # Test adding votes
- instance.rating.add(score=2, user=None, ip_address='127.0.0.2')
- self.assertEquals(instance.rating.score, 3)
- self.assertEquals(instance.rating.votes, 2)
-
- # Test changing of votes
- instance.rating.add(score=2, user=None, ip_address='127.0.0.1')
- self.assertEquals(instance.rating.score, 4)
- self.assertEquals(instance.rating.votes, 2)
-
- # Test users
- user = User.objects.create(username=str(random.randint(0, 100000000)))
- user2 = User.objects.create(username=str(random.randint(0, 100000000)))
-
- instance.rating.add(score=2, user=user, ip_address='127.0.0.3')
- self.assertEquals(instance.rating.score, 6)
- self.assertEquals(instance.rating.votes, 3)
-
- instance.rating2.add(score=2, user=user, ip_address='127.0.0.3')
- self.assertEquals(instance.rating2.score, 2)
- self.assertEquals(instance.rating2.votes, 1)
-
- self.assertRaises(IPLimitReached, instance.rating2.add,
- score=2, user=user2, ip_address='127.0.0.3')
-
- # Test deletion hooks
- Vote.objects.filter(ip_address='127.0.0.3').delete()
-
- instance = RatingTestModel.objects.get(pk=instance.pk)
-
- self.assertEquals(instance.rating.score, 4)
- self.assertEquals(instance.rating.votes, 2)
- self.assertEquals(instance.rating2.score, 0)
- self.assertEquals(instance.rating2.votes, 0)
-
-
-class RecommendationsTestCase(unittest.TestCase):
-
- def setUp(self):
- self.instance = RatingTestModel.objects.create()
- self.instance2 = RatingTestModel.objects.create()
- self.instance3 = RatingTestModel.objects.create()
- self.instance4 = RatingTestModel.objects.create()
- self.instance5 = RatingTestModel.objects.create()
-
- # Test users
- self.user = User.objects.create(
- username=str(random.randint(0, 100000000)))
- self.user2 = User.objects.create(
- username=str(random.randint(0, 100000000)))
-
- def testExclusions(self):
- Vote.objects.all().delete()
-
- self.instance.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance2.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance3.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance4.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance5.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
-
- # we should only need to call this once
- SimilarUser.objects.update_recommendations()
-
- self.assertEquals(SimilarUser.objects.count(), 2)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 4)
-
- ct = ContentType.objects.get_for_model(RatingTestModel)
-
- IgnoredObject.objects.create(
- user=self.user2, content_type=ct, object_id=self.instance2.pk)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 3)
-
- IgnoredObject.objects.create(
- user=self.user2, content_type=ct, object_id=self.instance3.pk)
- IgnoredObject.objects.create(
- user=self.user2, content_type=ct, object_id=self.instance4.pk)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 1)
- self.assertEquals(recs, [self.instance5])
-
- self.instance5.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 0)
-
- def testSimilarUsers(self):
- Vote.objects.all().delete()
-
- self.instance.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance2.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance3.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance4.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance5.rating.add(
- score=1, user=self.user, ip_address='127.0.0.1')
- self.instance.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
- self.instance2.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
- self.instance3.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
-
- SimilarUser.objects.update_recommendations()
-
- self.assertEquals(SimilarUser.objects.count(), 2)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 2)
-
- self.instance4.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
-
- SimilarUser.objects.update_recommendations()
-
- self.assertEquals(SimilarUser.objects.count(), 2)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 1)
- self.assertEquals(recs, [self.instance5])
-
- self.instance5.rating.add(
- score=1, user=self.user2, ip_address='127.0.0.2')
-
- SimilarUser.objects.update_recommendations()
-
- self.assertEquals(SimilarUser.objects.count(), 2)
-
- recs = list(SimilarUser.objects.get_recommendations(
- self.user2, RatingTestModel))
- self.assertEquals(len(recs), 0)
=== removed file 'djangoratings/views.py'
--- djangoratings/views.py 2016-12-13 18:28:51 +0000
+++ djangoratings/views.py 1970-01-01 00:00:00 +0000
@@ -1,138 +0,0 @@
-from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import ObjectDoesNotExist
-from django.http import HttpResponse, Http404
-
-from exceptions import *
-from django.conf import settings
-from default_settings import RATINGS_VOTES_PER_IP
-from wl_utils import get_real_ip
-
-
-class AddRatingView(object):
-
- def __call__(self, request, content_type_id, object_id, field_name, score):
- """__call__(request, content_type_id, object_id, field_name, score)
-
- Adds a vote to the specified model field.
-
- """
-
- try:
- instance = self.get_instance(content_type_id, object_id)
- except ObjectDoesNotExist:
- raise Http404('Object does not exist')
-
- context = self.get_context(request)
- context['instance'] = instance
-
- try:
- field = getattr(instance, field_name)
- except AttributeError:
- return self.invalid_field_response(request, context)
-
- context.update({
- 'field': field,
- 'score': score,
- })
-
- had_voted = bool(field.get_rating_for_user(
- request.user, get_real_ip(request), request.COOKIES))
-
- context['had_voted'] = had_voted
-
- try:
- adds = field.add(score, request.user,
- get_real_ip(request), request.COOKIES)
- except IPLimitReached:
- return self.too_many_votes_from_ip_response(request, context)
- except AuthRequired:
- return self.authentication_required_response(request, context)
- except InvalidRating:
- return self.invalid_rating_response(request, context)
- except CannotChangeVote:
- return self.cannot_change_vote_response(request, context)
- except CannotDeleteVote:
- return self.cannot_delete_vote_response(request, context)
- if had_voted:
- return self.rating_changed_response(request, context, adds)
- return self.rating_added_response(request, context, adds)
-
- def get_context(self, request, context={}):
- return context
-
- def render_to_response(self, template, context, request):
- raise NotImplementedError
-
- def too_many_votes_from_ip_response(self, request, context):
- response = HttpResponse(
- 'Too many votes from this IP address for this object.')
- return response
-
- def rating_changed_response(self, request, context, adds={}):
- response = HttpResponse('Vote changed.')
- if 'cookie' in adds:
- cookie_name, cookie = adds['cookie_name'], adds['cookie']
- if 'deleted' in adds:
- response.delete_cookie(cookie_name)
- else:
- # TODO: move cookie max_age to settings
- response.set_cookie(cookie_name, cookie, 31536000, path='/')
- return response
-
- def rating_added_response(self, request, context, adds={}):
- response = HttpResponse('Vote recorded.')
- if 'cookie' in adds:
- cookie_name, cookie = adds['cookie_name'], adds['cookie']
- if 'deleted' in adds:
- response.delete_cookie(cookie_name)
- else:
- # TODO: move cookie max_age to settings
- response.set_cookie(cookie_name, cookie, 31536000, path='/')
- return response
-
- def authentication_required_response(self, request, context):
- response = HttpResponse('You must be logged in to vote.')
- response.status_code = 403
- return response
-
- def cannot_change_vote_response(self, request, context):
- response = HttpResponse('You have already voted.')
- response.status_code = 403
- return response
-
- def cannot_delete_vote_response(self, request, context):
- response = HttpResponse('You can\'t delete this vote.')
- response.status_code = 403
- return response
-
- def invalid_field_response(self, request, context):
- response = HttpResponse('Invalid field name.')
- response.status_code = 403
- return response
-
- def invalid_rating_response(self, request, context):
- response = HttpResponse('Invalid rating value.')
- response.status_code = 403
- return response
-
- def get_instance(self, content_type_id, object_id):
- return ContentType.objects.get(pk=content_type_id)\
- .get_object_for_this_type(pk=object_id)
-
-
-class AddRatingFromModel(AddRatingView):
-
- def __call__(self, request, model, app_label, object_id, field_name, score):
- """__call__(request, model, app_label, object_id, field_name, score)
-
- Adds a vote to the specified model field.
-
- """
- try:
- content_type = ContentType.objects.get(
- model=model, app_label=app_label)
- except ContentType.DoesNotExist:
- raise Http404('Invalid `model` or `app_label`.')
-
- return super(AddRatingFromModel, self).__call__(request, content_type.id,
- object_id, field_name, score)
=== modified file 'media/css/base.css'
--- media/css/base.css 2018-10-15 07:13:24 +0000
+++ media/css/base.css 2018-11-18 17:28:32 +0000
@@ -108,6 +108,10 @@
font-weight: bold;
}
+.star-ratings input, button {
+ padding: inherit;
+}
+
textarea {
font-weight: normal;
font-size: 0.9em;
=== modified file 'pip_requirements.txt'
--- pip_requirements.txt 2018-11-05 17:12:12 +0000
+++ pip_requirements.txt 2018-11-18 17:28:32 +0000
@@ -7,6 +7,7 @@
# Do not install newer versions because our notifications app is affected
-e git://github.com/arneb/django-messages.git@2d8dabb755e0b5ace876bde25f45d07c2051ac37#egg=django_messages
django-nocaptcha-recaptcha==0.0.19
+django-star-ratings==0.7.0
dj-pagination==2.3.2
django-registration==2.4.1
django-tagging==0.4.5
=== modified file 'settings.py'
--- settings.py 2018-11-12 19:36:22 +0000
+++ settings.py 2018-11-18 17:28:32 +0000
@@ -110,7 +110,7 @@
'django_messages_wl.apps.WLDjangoMessagesConfig',
'dj_pagination',
'tagging',
- 'djangoratings', # included as wlapp
+ 'star_ratings',
]
MIDDLEWARE = [
@@ -333,6 +333,14 @@
# See: https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-age
CSRF_COOKIE_AGE = None
+##############################
+# star_rarting configuration #
+##############################
+
+STAR_RATINGS_STAR_HEIGHT = 14
+STAR_RATINGS_STAR_WIDTH = 14
+STAR_RATINGS_RANGE = 10
+
try:
from local_settings import *
except ImportError:
=== added directory 'templates/star_rating'
=== added file 'templates/star_rating/average.html'
--- templates/star_rating/average.html 1970-01-01 00:00:00 +0000
+++ templates/star_rating/average.html 2018-11-18 17:28:32 +0000
@@ -0,0 +1,7 @@
+{% extends "star_ratings/widget_base.html" %}
+{% block rating_stars %}
+ {# No stars here #}
+{% endblock %}
+{% block rating_detail %}
+{{ rating.average|floatformat:"-2" }} ({{ rating.count }} Vote{{ rating.count|pluralize }})
+{% endblock %}
\ No newline at end of file
=== added file 'templates/star_rating/rate.html'
--- templates/star_rating/rate.html 1970-01-01 00:00:00 +0000
+++ templates/star_rating/rate.html 2018-11-18 17:28:32 +0000
@@ -0,0 +1,27 @@
+{% extends "star_ratings/widget_base.html" %}
+{% load i18n %}
+
+{% block rating_detail %}
+{% if not user.is_authenticated and not anonymous_ratings and not read_only %}
+ <p>
+ {{ rating.average|floatformat:"-2" }} ({{ rating.count }} Vote{{ rating.count|pluralize }})
+ <a href="{% url 'auth_login' %}?next={{ request.path }}">{% trans 'Please log in to vote.' %}</a>
+ </p>
+{% else %}
+ {% block rating_user %}
+ <p class="star-ratings-rating-user">
+ {{ rating.average|floatformat:"-2" }} ({{ rating.count }} Vote{{ rating.count|pluralize }}) -
+ {% trans 'You voted: ' %}
+ <span class='star-ratings-rating-value'>
+ {% if user_rating %}
+ {{ user_rating.score }}
+ {% else %}
+ {% trans 'Not voted' %}
+ {% endif %}
+ </span>
+ </p>
+ {% endblock rating_user %}
+ <p class="star-ratings-errors"><p>
+{% endif %}
+{% endblock rating_detail %}
+
=== modified file 'templates/wlmaps/base.html'
--- templates/wlmaps/base.html 2015-02-18 22:30:08 +0000
+++ templates/wlmaps/base.html 2018-11-18 17:28:32 +0000
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load static %}
{% comment %}
vim:ft=htmldjango
@@ -7,6 +8,8 @@
{% block extra_head %}
<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/forum.css" />
<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/maps.css" />
+<link rel="stylesheet" href="{% static 'star-ratings/css/star-ratings.css' %}">
+<script type="text/javascript" src="{% static 'star-ratings/js/dist/star-ratings.min.js' %}"></script>
{{block.super}}
{% endblock %}
=== modified file 'templates/wlmaps/index.html'
--- templates/wlmaps/index.html 2018-10-14 13:24:15 +0000
+++ templates/wlmaps/index.html 2018-11-18 17:28:32 +0000
@@ -5,9 +5,9 @@
{% load custom_date %}
{% load wlprofile_extras %}
-{% load wlmaps_extra %}
{% load threadedcommentstags %}
{% load pagination_tags %}
+{% load ratings %}
{% block content_header %}
<h1>Maps</h1>
@@ -55,7 +55,9 @@
</tr>
<tr>
<td class="grey">Rating:</td>
- <td>{{ map.rating|average_rating }} ({{ map.rating.votes }} Votes)</td>
+ <td>
+ {% ratings map read_only template_name='star_rating/average.html' %}
+ </td>
<td class="spacer"></td>
{% get_comment_count for map as ccount %}
<td class="grey">Comments:</td><td>{{ ccount }}</td>
=== modified file 'templates/wlmaps/map_detail.html'
--- templates/wlmaps/map_detail.html 2018-10-14 13:24:15 +0000
+++ templates/wlmaps/map_detail.html 2018-11-18 17:28:32 +0000
@@ -4,32 +4,16 @@
{% endcomment %}
{% load custom_date %}
-{% load wlmaps_extra %}
{% load wlprofile_extras %}
{% load threadedcommentstags %}
{% load wl_markdown %}
+{% load ratings %}
{% block title %}{{ map.name }} - {{ block.super }}{% endblock %}
{% block extra_head %}
{{ block.super }}
-<link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/comments.css" />
-{% if not user.is_anonymous %}
-<script src="{{ MEDIA_URL}}/js/jquery.sexy-vote.js" type="text/javascript"></script>
-<script type="text/javascript">
-$(function() {
- $('#vote').sexyVote( {
- activeImageSrc: "{{ MEDIA_URL }}img/active_star.gif",
- passiveImageSrc: "{{ MEDIA_URL }}img/passive_star.gif",
- maxScore: 10,
- messages: ["","","","","","","","","",""],
- fn: function(e, score) {
- $.post("{% url 'wlmaps_rate' map.slug %}",{ vote: score });
- }
- });
-});
-</script>
-{% endif %}
+ <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/comments.css" />
{% endblock %}
{% block content_header %}
@@ -95,12 +79,7 @@
<tr>
<td class="grey">Rating:</td>
<td>
- {{ map.rating|average_rating }} ({{ map.rating.votes }} Votes)
- {% if not user.is_anonymous %}
- <span id="vote"></span>
- {% else %}
- - Login to vote
- {% endif %}
+ {% ratings map template_name='star_rating/rate.html' %}
</td>
</tr>
<tr>
=== modified file 'urls.py'
--- urls.py 2018-10-10 17:04:53 +0000
+++ urls.py 2018-11-18 17:28:32 +0000
@@ -27,6 +27,7 @@
url(r'^accounts/', include('registration.backends.hmac.urls')),
url('^', include('django.contrib.auth.urls')),
+ url(r'^ratings/', include('star_ratings.urls', namespace='ratings', app_name='ratings')),
# Formerly 3rd party
url(r'^notification/', include('notification.urls')),
=== added file 'wlmaps/migrations/0002_auto_20181118_1758.py'
--- wlmaps/migrations/0002_auto_20181118_1758.py 1970-01-01 00:00:00 +0000
+++ wlmaps/migrations/0002_auto_20181118_1758.py 2018-11-18 17:28:32 +0000
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.12 on 2018-11-18 17:58
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wlmaps', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='map',
+ name='rating_score',
+ ),
+ migrations.RemoveField(
+ model_name='map',
+ name='rating_votes',
+ ),
+ ]
=== modified file 'wlmaps/models.py'
--- wlmaps/models.py 2018-04-08 16:23:55 +0000
+++ wlmaps/models.py 2018-11-18 17:28:32 +0000
@@ -11,8 +11,6 @@
except ImportError:
notification = None
-from djangoratings.fields import AnonymousRatingField
-
class Map(models.Model):
name = models.CharField(max_length=255, unique=True)
@@ -38,7 +36,6 @@
nr_downloads = models.PositiveIntegerField(
verbose_name='Download count', default=0)
- rating = AnonymousRatingField(range=10, can_change_vote=True)
class Meta:
ordering = ('-pub_date',)
=== removed directory 'wlmaps/templatetags'
=== removed file 'wlmaps/templatetags/__init__.py'
=== removed file 'wlmaps/templatetags/wlmaps_extra.py'
--- wlmaps/templatetags/wlmaps_extra.py 2016-12-13 18:28:51 +0000
+++ wlmaps/templatetags/wlmaps_extra.py 1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
-#!/usr/bin/env python -tt
-# encoding: utf-8
-
-from django import template
-
-register = template.Library()
-
-
-@register.filter
-def average_rating(rating):
- if rating.votes > 0:
- avg = '%.1f' % (float(rating.score) / rating.votes)
- else:
- avg = '0.0'
- return avg
=== modified file 'wlmaps/tests/test_views.py'
--- wlmaps/tests/test_views.py 2018-04-08 14:40:17 +0000
+++ wlmaps/tests/test_views.py 2018-11-18 17:28:32 +0000
@@ -143,66 +143,3 @@
reverse('wlmaps_view', args=('a-map-that-doesnt-exist',)))
self.assertEqual(c.status_code, 404)
-
-############
-# RATING #
-############
-class TestWLMapsViews_Rating(_LoginToSite):
-
- def setUp(self):
- _LoginToSite.setUp(self)
-
- # Add maps
- nm = Map.objects.create(
- name='Map',
- author='Author',
- w=128,
- h=64,
- nr_players=4,
- descr='a good map to play with',
- minimap='/wlmaps/minimaps/Map.png',
- world_name='blackland',
-
- uploader=self.user,
- uploader_comment='Rockdamap'
- )
- nm.save()
- self.map = nm
-
- def test_RatingNonExistingMap_Except404(self):
- c = self.client.post(
- reverse('wlmaps_rate', args=('a-map-that-doesnt-exist',)),
- {'vote': 10})
- self.assertEqual(c.status_code, 404)
-
- def test_RatingGet_Except405(self):
- c = self.client.get(
- reverse('wlmaps_rate', args=('map',)),
- {'vote': 10})
- self.assertEqual(c.status_code, 405)
-
- def test_RatingInvalidValue_Except400(self):
- c = self.client.post(
- reverse('wlmaps_rate', args=('map',)),
- {'vote': 11})
- self.assertEqual(c.status_code, 400)
-
- def test_RatingNonIntegerValue_Except400(self):
- c = self.client.post(
- reverse('wlmaps_rate', args=('map',)),
- {'vote': 'shubidu'})
- self.assertEqual(c.status_code, 400)
-
- def test_RatingExistingMap_ExceptCorrectResult(self):
- c = self.client.post(
- reverse('wlmaps_rate', args=('map',)),
- {'vote': 7})
- # Except redirect
- self.assertEqual(c.status_code, 302)
-
- # We have to refetch this map, because
- # votes doesn't hit the database
- m = Map.objects.get(slug='map')
-
- self.assertEqual(m.rating.votes, 1)
- self.assertEqual(m.rating.score, 7)
=== modified file 'wlmaps/urls.py'
--- wlmaps/urls.py 2016-12-13 18:28:51 +0000
+++ wlmaps/urls.py 2018-11-18 17:28:32 +0000
@@ -4,6 +4,7 @@
from models import Map
from views import *
+
urlpatterns = [
url(r'^$', index, name='wlmaps_index'),
url(r'^upload/$', upload, name='wlmaps_upload'),
@@ -14,7 +15,4 @@
edit_comment, name='wlmaps_edit_comment'),
url(r'^(?P<map_slug>[-\w]+)/download/$',
download, name='wlmaps_download'),
-
- url(r'^(?P<map_slug>[-\w]+)/rate/$',
- rate, name='wlmaps_rate'),
]
=== modified file 'wlmaps/views.py'
--- wlmaps/views.py 2018-04-08 14:40:17 +0000
+++ wlmaps/views.py 2018-11-18 17:28:32 +0000
@@ -24,31 +24,6 @@
})
-def rate(request, map_slug):
- """Rate a given map."""
- if request.method != 'POST':
- return HttpResponseNotAllowed(['post'])
-
- m = get_object_or_404(models.Map, slug=map_slug)
-
- if not 'vote' in request.POST:
- return HttpResponseBadRequest()
- try:
- val = int(request.POST['vote'])
- except ValueError:
- return HttpResponseBadRequest()
-
- if not (0 < val <= 10):
- return HttpResponseBadRequest()
-
- m.rating.add(score=val, user=request.user,
- ip_address=get_real_ip(request))
-
- # m.save() is called in djangoratings.fields.add for each instance
-
- return HttpResponseRedirect(reverse('wlmaps_view', kwargs= {'map_slug': m.slug}))
-
-
def download(request, map_slug):
"""Very simple view that just returns the binary data of this map and
increases the download count."""
Follow ups