Merge lp:~widelands-dev/widelands-website/remove_djangoratings into lp:widelands-website
- remove_djangoratings
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 509 | ||||
Proposed branch: | lp:~widelands-dev/widelands-website/remove_djangoratings | ||||
Merge into: | lp:widelands-website | ||||
Diff against target: |
1747 lines (+40/-1472) 26 files modified
djangoratings/LICENSE (+0/-22) djangoratings/__init__.py (+0/-50) djangoratings/admin.py (+0/-18) djangoratings/default_settings.py (+0/-5) djangoratings/exceptions.py (+0/-18) djangoratings/fields.py (+0/-434) djangoratings/forms.py (+0/-7) djangoratings/management/commands/update_recommendations.py (+0/-9) djangoratings/managers.py (+0/-124) djangoratings/migrations/0001_initial.py (+0/-90) djangoratings/models.py (+0/-98) djangoratings/runtests.py (+0/-31) djangoratings/templatetags/ratings_old.py (+0/-101) djangoratings/tests.py (+0/-184) djangoratings/views.py (+0/-138) media/css/base.css (+1/-1) settings.py (+0/-1) templates/wlmaps/base.html (+1/-2) templates/wlmaps/index.html (+1/-3) templates/wlmaps/map_detail.html (+3/-27) wlmaps/migrations/0002_auto_20181119_1855.py (+33/-0) wlmaps/models.py (+0/-3) wlmaps/templatetags/wlmaps_extra.py (+0/-15) wlmaps/tests/test_views.py (+0/-63) wlmaps/urls.py (+1/-3) wlmaps/views.py (+0/-25) |
||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands-website/remove_djangoratings | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+358960@code.launchpad.net |
Commit message
Remove djangoratings
Description of the change
Follow up of https:/
Bye, bye djangoratings :-D
kaputtnik (franku) wrote : | # |
This is merged for now, but not commited. There is a problem showing the stars for voting, which i couldn't solve so far. At home all is fine, but on the server i struggle with the served star images...
kaputtnik (franku) wrote : | # |
Ok, this is merged and deployed now. I will explain in another merge request why this didn't worked right away.
What remains is the deletion of the djangoratings related tables in the mysql Database. But i saw on the server there are several old tables in the Database. No idea if having unused tables in the Database do affect the performance. I will create a new bugreport for this issue.
GunChleoc (gunchleoc) wrote : | # |
Cleaning up unused stuff is always a good idea :)
Preview Diff
1 | === removed directory 'djangoratings' | |||
2 | === removed file 'djangoratings/LICENSE' | |||
3 | --- djangoratings/LICENSE 2016-05-18 19:31:46 +0000 | |||
4 | +++ djangoratings/LICENSE 1970-01-01 00:00:00 +0000 | |||
5 | @@ -1,22 +0,0 @@ | |||
6 | 1 | Copyright (c) 2009, David Cramer <dcramer@gmail.com> | ||
7 | 2 | All rights reserved. | ||
8 | 3 | |||
9 | 4 | Redistribution and use in source and binary forms, with or without modification, | ||
10 | 5 | are permitted provided that the following conditions are met: | ||
11 | 6 | |||
12 | 7 | * Redistributions of source code must retain the above copyright notice, this | ||
13 | 8 | list of conditions and the following disclaimer. | ||
14 | 9 | * Redistributions in binary form must reproduce the above copyright notice, | ||
15 | 10 | this list of conditions and the following disclaimer in the documentation | ||
16 | 11 | and/or other materials provided with the distribution. | ||
17 | 12 | |||
18 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
19 | 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
20 | 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
21 | 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
22 | 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
23 | 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
24 | 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
25 | 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
26 | 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
27 | 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | 23 | 0 | ||
29 | === removed file 'djangoratings/__init__.py' | |||
30 | --- djangoratings/__init__.py 2016-12-13 18:28:51 +0000 | |||
31 | +++ djangoratings/__init__.py 1970-01-01 00:00:00 +0000 | |||
32 | @@ -1,50 +0,0 @@ | |||
33 | 1 | import os.path | ||
34 | 2 | import warnings | ||
35 | 3 | |||
36 | 4 | __version__ = (0, 3, 7) | ||
37 | 5 | |||
38 | 6 | |||
39 | 7 | def _get_git_revision(path): | ||
40 | 8 | revision_file = os.path.join(path, 'refs', 'heads', 'master') | ||
41 | 9 | if not os.path.exists(revision_file): | ||
42 | 10 | return None | ||
43 | 11 | fh = open(revision_file, 'r') | ||
44 | 12 | try: | ||
45 | 13 | return fh.read() | ||
46 | 14 | finally: | ||
47 | 15 | fh.close() | ||
48 | 16 | |||
49 | 17 | |||
50 | 18 | def get_revision(): | ||
51 | 19 | """ | ||
52 | 20 | :returns: Revision number of this branch/checkout, if available. None if | ||
53 | 21 | no revision number can be determined. | ||
54 | 22 | """ | ||
55 | 23 | package_dir = os.path.dirname(__file__) | ||
56 | 24 | checkout_dir = os.path.normpath(os.path.join(package_dir, '..')) | ||
57 | 25 | path = os.path.join(checkout_dir, '.git') | ||
58 | 26 | if os.path.exists(path): | ||
59 | 27 | return _get_git_revision(path) | ||
60 | 28 | return None | ||
61 | 29 | |||
62 | 30 | __build__ = get_revision() | ||
63 | 31 | |||
64 | 32 | |||
65 | 33 | def lazy_object(location): | ||
66 | 34 | def inner(*args, **kwargs): | ||
67 | 35 | parts = location.rsplit('.', 1) | ||
68 | 36 | warnings.warn('`djangoratings.%s` is deprecated. Please use `%s` instead.' % ( | ||
69 | 37 | parts[1], location), DeprecationWarning) | ||
70 | 38 | try: | ||
71 | 39 | imp = __import__(parts[0], globals(), locals(), [parts[1]], -1) | ||
72 | 40 | except: | ||
73 | 41 | imp = __import__(parts[0], globals(), locals(), [parts[1]]) | ||
74 | 42 | func = getattr(imp, parts[1]) | ||
75 | 43 | if callable(func): | ||
76 | 44 | return func(*args, **kwargs) | ||
77 | 45 | return func | ||
78 | 46 | return inner | ||
79 | 47 | |||
80 | 48 | RatingField = lazy_object('djangoratings.fields.RatingField') | ||
81 | 49 | AnonymousRatingField = lazy_object('djangoratings.fields.AnonymousRatingField') | ||
82 | 50 | Rating = lazy_object('djangoratings.fields.Rating') | ||
83 | 51 | 0 | ||
84 | === removed file 'djangoratings/admin.py' | |||
85 | --- djangoratings/admin.py 2016-12-13 18:28:51 +0000 | |||
86 | +++ djangoratings/admin.py 1970-01-01 00:00:00 +0000 | |||
87 | @@ -1,18 +0,0 @@ | |||
88 | 1 | from django.contrib import admin | ||
89 | 2 | from models import Vote, Score | ||
90 | 3 | |||
91 | 4 | |||
92 | 5 | class VoteAdmin(admin.ModelAdmin): | ||
93 | 6 | list_display = ('content_object', 'user', 'ip_address', | ||
94 | 7 | 'cookie', 'score', 'date_changed') | ||
95 | 8 | list_filter = ('score', 'content_type', 'date_changed') | ||
96 | 9 | search_fields = ('ip_address',) | ||
97 | 10 | raw_id_fields = ('user',) | ||
98 | 11 | |||
99 | 12 | |||
100 | 13 | class ScoreAdmin(admin.ModelAdmin): | ||
101 | 14 | list_display = ('content_object', 'score', 'votes') | ||
102 | 15 | list_filter = ('content_type',) | ||
103 | 16 | |||
104 | 17 | admin.site.register(Vote, VoteAdmin) | ||
105 | 18 | admin.site.register(Score, ScoreAdmin) | ||
106 | 19 | 0 | ||
107 | === removed file 'djangoratings/default_settings.py' | |||
108 | --- djangoratings/default_settings.py 2016-12-13 18:28:51 +0000 | |||
109 | +++ djangoratings/default_settings.py 1970-01-01 00:00:00 +0000 | |||
110 | @@ -1,5 +0,0 @@ | |||
111 | 1 | from django.conf import settings | ||
112 | 2 | |||
113 | 3 | # Used to limit the number of unique IPs that can vote on a single object+field. | ||
114 | 4 | # useful if you're getting rating spam by users registering multiple accounts | ||
115 | 5 | RATINGS_VOTES_PER_IP = 3 | ||
116 | 6 | 0 | ||
117 | === removed file 'djangoratings/exceptions.py' | |||
118 | --- djangoratings/exceptions.py 2016-12-13 18:28:51 +0000 | |||
119 | +++ djangoratings/exceptions.py 1970-01-01 00:00:00 +0000 | |||
120 | @@ -1,18 +0,0 @@ | |||
121 | 1 | class InvalidRating(ValueError): | ||
122 | 2 | pass | ||
123 | 3 | |||
124 | 4 | |||
125 | 5 | class AuthRequired(TypeError): | ||
126 | 6 | pass | ||
127 | 7 | |||
128 | 8 | |||
129 | 9 | class CannotChangeVote(Exception): | ||
130 | 10 | pass | ||
131 | 11 | |||
132 | 12 | |||
133 | 13 | class CannotDeleteVote(Exception): | ||
134 | 14 | pass | ||
135 | 15 | |||
136 | 16 | |||
137 | 17 | class IPLimitReached(Exception): | ||
138 | 18 | pass | ||
139 | 19 | 0 | ||
140 | === removed file 'djangoratings/fields.py' | |||
141 | --- djangoratings/fields.py 2018-04-08 14:29:44 +0000 | |||
142 | +++ djangoratings/fields.py 1970-01-01 00:00:00 +0000 | |||
143 | @@ -1,434 +0,0 @@ | |||
144 | 1 | from django.db.models import IntegerField, PositiveIntegerField | ||
145 | 2 | from django.conf import settings | ||
146 | 3 | |||
147 | 4 | import forms | ||
148 | 5 | import itertools | ||
149 | 6 | from datetime import datetime | ||
150 | 7 | |||
151 | 8 | from models import Vote, Score | ||
152 | 9 | from default_settings import RATINGS_VOTES_PER_IP | ||
153 | 10 | from exceptions import * | ||
154 | 11 | |||
155 | 12 | if 'django.contrib.contenttypes' not in settings.INSTALLED_APPS: | ||
156 | 13 | raise ImportError( | ||
157 | 14 | 'djangoratings requires django.contrib.contenttypes in your INSTALLED_APPS') | ||
158 | 15 | |||
159 | 16 | from django.contrib.contenttypes.models import ContentType | ||
160 | 17 | |||
161 | 18 | __all__ = ('Rating', 'RatingField', 'AnonymousRatingField') | ||
162 | 19 | |||
163 | 20 | try: | ||
164 | 21 | from hashlib import md5 | ||
165 | 22 | except ImportError: | ||
166 | 23 | from md5 import new as md5 | ||
167 | 24 | |||
168 | 25 | try: | ||
169 | 26 | from django.utils.timezone import now | ||
170 | 27 | except ImportError: | ||
171 | 28 | now = datetime.now | ||
172 | 29 | |||
173 | 30 | |||
174 | 31 | def md5_hexdigest(value): | ||
175 | 32 | return md5(value).hexdigest() | ||
176 | 33 | |||
177 | 34 | |||
178 | 35 | class Rating(object): | ||
179 | 36 | |||
180 | 37 | def __init__(self, score, votes): | ||
181 | 38 | self.score = score | ||
182 | 39 | self.votes = votes | ||
183 | 40 | |||
184 | 41 | |||
185 | 42 | class RatingManager(object): | ||
186 | 43 | |||
187 | 44 | def __init__(self, instance, field): | ||
188 | 45 | self.content_type = None | ||
189 | 46 | self.instance = instance | ||
190 | 47 | self.field = field | ||
191 | 48 | |||
192 | 49 | self.votes_field_name = '%s_votes' % (self.field.name,) | ||
193 | 50 | self.score_field_name = '%s_score' % (self.field.name,) | ||
194 | 51 | |||
195 | 52 | def get_percent(self): | ||
196 | 53 | """get_percent() | ||
197 | 54 | |||
198 | 55 | Returns the weighted percentage of the score from min-max values | ||
199 | 56 | |||
200 | 57 | """ | ||
201 | 58 | if not (self.votes and self.score): | ||
202 | 59 | return 0 | ||
203 | 60 | return 100 * (self.get_rating() / self.field.range) | ||
204 | 61 | |||
205 | 62 | def get_real_percent(self): | ||
206 | 63 | """get_real_percent() | ||
207 | 64 | |||
208 | 65 | Returns the unmodified percentage of the score based on a 0-point scale. | ||
209 | 66 | |||
210 | 67 | """ | ||
211 | 68 | if not (self.votes and self.score): | ||
212 | 69 | return 0 | ||
213 | 70 | return 100 * (self.get_real_rating() / self.field.range) | ||
214 | 71 | |||
215 | 72 | def get_ratings(self): | ||
216 | 73 | """get_ratings() | ||
217 | 74 | |||
218 | 75 | Returns a Vote QuerySet for this rating field. | ||
219 | 76 | |||
220 | 77 | """ | ||
221 | 78 | return Vote.objects.filter(content_type=self.get_content_type(), object_id=self.instance.pk, key=self.field.key) | ||
222 | 79 | |||
223 | 80 | def get_rating(self): | ||
224 | 81 | """get_rating() | ||
225 | 82 | |||
226 | 83 | Returns the weighted average rating. | ||
227 | 84 | |||
228 | 85 | """ | ||
229 | 86 | if not (self.votes and self.score): | ||
230 | 87 | return 0 | ||
231 | 88 | return float(self.score) / (self.votes + self.field.weight) | ||
232 | 89 | |||
233 | 90 | def get_opinion_percent(self): | ||
234 | 91 | """get_opinion_percent() | ||
235 | 92 | |||
236 | 93 | Returns a neutral-based percentage. | ||
237 | 94 | |||
238 | 95 | """ | ||
239 | 96 | return (self.get_percent() + 100) / 2 | ||
240 | 97 | |||
241 | 98 | def get_real_rating(self): | ||
242 | 99 | """get_rating() | ||
243 | 100 | |||
244 | 101 | Returns the unmodified average rating. | ||
245 | 102 | |||
246 | 103 | """ | ||
247 | 104 | if not (self.votes and self.score): | ||
248 | 105 | return 0 | ||
249 | 106 | return float(self.score) / self.votes | ||
250 | 107 | |||
251 | 108 | def get_rating_for_user(self, user, ip_address=None, cookies={}): | ||
252 | 109 | """get_rating_for_user(user, ip_address=None, cookie=None) | ||
253 | 110 | |||
254 | 111 | Returns the rating for a user or anonymous IP.""" | ||
255 | 112 | kwargs = dict( | ||
256 | 113 | content_type=self.get_content_type(), | ||
257 | 114 | object_id=self.instance.pk, | ||
258 | 115 | key=self.field.key, | ||
259 | 116 | ) | ||
260 | 117 | |||
261 | 118 | if not (user and user.is_authenticated): | ||
262 | 119 | if not ip_address: | ||
263 | 120 | raise ValueError('``user`` or ``ip_address`` must be present.') | ||
264 | 121 | kwargs['user__isnull'] = True | ||
265 | 122 | kwargs['ip_address'] = ip_address | ||
266 | 123 | else: | ||
267 | 124 | kwargs['user'] = user | ||
268 | 125 | |||
269 | 126 | use_cookies = (self.field.allow_anonymous and self.field.use_cookies) | ||
270 | 127 | if use_cookies: | ||
271 | 128 | # TODO: move 'vote-%d.%d.%s' to settings or something | ||
272 | 129 | cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[ | ||
273 | 130 | 'object_id'], kwargs['key'][:6],) # -> md5_hexdigest? | ||
274 | 131 | cookie = cookies.get(cookie_name) | ||
275 | 132 | if cookie: | ||
276 | 133 | kwargs['cookie'] = cookie | ||
277 | 134 | else: | ||
278 | 135 | kwargs['cookie__isnull'] = True | ||
279 | 136 | |||
280 | 137 | try: | ||
281 | 138 | rating = Vote.objects.get(**kwargs) | ||
282 | 139 | return rating.score | ||
283 | 140 | except Vote.MultipleObjectsReturned: | ||
284 | 141 | pass | ||
285 | 142 | except Vote.DoesNotExist: | ||
286 | 143 | pass | ||
287 | 144 | return | ||
288 | 145 | |||
289 | 146 | def get_iterable_range(self): | ||
290 | 147 | # started from 1, because 0 is equal to delete | ||
291 | 148 | return range(1, self.field.range) | ||
292 | 149 | |||
293 | 150 | def add(self, score, user, ip_address, cookies={}, commit=True): | ||
294 | 151 | """add(score, user, ip_address) | ||
295 | 152 | |||
296 | 153 | Used to add a rating to an object. | ||
297 | 154 | |||
298 | 155 | """ | ||
299 | 156 | try: | ||
300 | 157 | score = int(score) | ||
301 | 158 | except (ValueError, TypeError): | ||
302 | 159 | raise InvalidRating('%s is not a valid choice for %s' % | ||
303 | 160 | (score, self.field.name)) | ||
304 | 161 | |||
305 | 162 | delete = (score == 0) | ||
306 | 163 | if delete and not self.field.allow_delete: | ||
307 | 164 | raise CannotDeleteVote( | ||
308 | 165 | 'you are not allowed to delete votes for %s' % (self.field.name,)) | ||
309 | 166 | # ... you're also can't delete your vote if you haven't permissions to change it. I leave this case for CannotChangeVote | ||
310 | 167 | |||
311 | 168 | if score < 0 or score > self.field.range: | ||
312 | 169 | raise InvalidRating('%s is not a valid choice for %s' % | ||
313 | 170 | (score, self.field.name)) | ||
314 | 171 | |||
315 | 172 | is_anonymous = (user is None or not user.is_authenticated) | ||
316 | 173 | if is_anonymous and not self.field.allow_anonymous: | ||
317 | 174 | raise AuthRequired("user must be a user, not '%r'" % (user,)) | ||
318 | 175 | |||
319 | 176 | if is_anonymous: | ||
320 | 177 | user = None | ||
321 | 178 | |||
322 | 179 | defaults = dict( | ||
323 | 180 | score=score, | ||
324 | 181 | ip_address=ip_address, | ||
325 | 182 | ) | ||
326 | 183 | |||
327 | 184 | kwargs = dict( | ||
328 | 185 | content_type=self.get_content_type(), | ||
329 | 186 | object_id=self.instance.pk, | ||
330 | 187 | key=self.field.key, | ||
331 | 188 | user=user, | ||
332 | 189 | ) | ||
333 | 190 | if not user: | ||
334 | 191 | kwargs['ip_address'] = ip_address | ||
335 | 192 | |||
336 | 193 | use_cookies = (self.field.allow_anonymous and self.field.use_cookies) | ||
337 | 194 | if use_cookies: | ||
338 | 195 | defaults['cookie'] = now().strftime( | ||
339 | 196 | '%Y%m%d%H%M%S%f') # -> md5_hexdigest? | ||
340 | 197 | # TODO: move 'vote-%d.%d.%s' to settings or something | ||
341 | 198 | cookie_name = 'vote-%d.%d.%s' % (kwargs['content_type'].pk, kwargs[ | ||
342 | 199 | 'object_id'], kwargs['key'][:6],) # -> md5_hexdigest? | ||
343 | 200 | # try to get existent cookie value | ||
344 | 201 | cookie = cookies.get(cookie_name) | ||
345 | 202 | if not cookie: | ||
346 | 203 | kwargs['cookie__isnull'] = True | ||
347 | 204 | kwargs['cookie'] = cookie | ||
348 | 205 | |||
349 | 206 | try: | ||
350 | 207 | rating, created = Vote.objects.get(**kwargs), False | ||
351 | 208 | except Vote.DoesNotExist: | ||
352 | 209 | if delete: | ||
353 | 210 | raise CannotDeleteVote( | ||
354 | 211 | 'attempt to find and delete your vote for %s is failed' % (self.field.name,)) | ||
355 | 212 | if getattr(settings, 'RATINGS_VOTES_PER_IP', RATINGS_VOTES_PER_IP): | ||
356 | 213 | num_votes = Vote.objects.filter( | ||
357 | 214 | content_type=kwargs['content_type'], | ||
358 | 215 | object_id=kwargs['object_id'], | ||
359 | 216 | key=kwargs['key'], | ||
360 | 217 | ip_address=ip_address, | ||
361 | 218 | ).count() | ||
362 | 219 | if num_votes >= getattr(settings, 'RATINGS_VOTES_PER_IP', RATINGS_VOTES_PER_IP): | ||
363 | 220 | raise IPLimitReached() | ||
364 | 221 | kwargs.update(defaults) | ||
365 | 222 | if use_cookies: | ||
366 | 223 | # record with specified cookie was not found ... | ||
367 | 224 | # ... thus we need to replace old cookie (if presented) with new one | ||
368 | 225 | cookie = defaults['cookie'] | ||
369 | 226 | # ... and remove 'cookie__isnull' (if presented) from .create()'s **kwargs | ||
370 | 227 | kwargs.pop('cookie__isnull', '') | ||
371 | 228 | rating, created = Vote.objects.create(**kwargs), True | ||
372 | 229 | |||
373 | 230 | has_changed = False | ||
374 | 231 | if not created: | ||
375 | 232 | if self.field.can_change_vote: | ||
376 | 233 | has_changed = True | ||
377 | 234 | self.score -= rating.score | ||
378 | 235 | # you can delete your vote only if you have permission to | ||
379 | 236 | # change your vote | ||
380 | 237 | if not delete: | ||
381 | 238 | rating.score = score | ||
382 | 239 | rating.save() | ||
383 | 240 | else: | ||
384 | 241 | self.votes -= 1 | ||
385 | 242 | rating.delete() | ||
386 | 243 | else: | ||
387 | 244 | raise CannotChangeVote() | ||
388 | 245 | else: | ||
389 | 246 | has_changed = True | ||
390 | 247 | self.votes += 1 | ||
391 | 248 | if has_changed: | ||
392 | 249 | if not delete: | ||
393 | 250 | self.score += rating.score | ||
394 | 251 | if commit: | ||
395 | 252 | self.instance.save() | ||
396 | 253 | #setattr(self.instance, self.field.name, Rating(score=self.score, votes=self.votes)) | ||
397 | 254 | |||
398 | 255 | defaults = dict( | ||
399 | 256 | score=self.score, | ||
400 | 257 | votes=self.votes, | ||
401 | 258 | ) | ||
402 | 259 | |||
403 | 260 | kwargs = dict( | ||
404 | 261 | content_type=self.get_content_type(), | ||
405 | 262 | object_id=self.instance.pk, | ||
406 | 263 | key=self.field.key, | ||
407 | 264 | ) | ||
408 | 265 | |||
409 | 266 | try: | ||
410 | 267 | score, created = Score.objects.get(**kwargs), False | ||
411 | 268 | except Score.DoesNotExist: | ||
412 | 269 | kwargs.update(defaults) | ||
413 | 270 | score, created = Score.objects.create(**kwargs), True | ||
414 | 271 | |||
415 | 272 | if not created: | ||
416 | 273 | score.__dict__.update(defaults) | ||
417 | 274 | score.save() | ||
418 | 275 | |||
419 | 276 | # return value | ||
420 | 277 | adds = {} | ||
421 | 278 | if use_cookies: | ||
422 | 279 | adds['cookie_name'] = cookie_name | ||
423 | 280 | adds['cookie'] = cookie | ||
424 | 281 | if delete: | ||
425 | 282 | adds['deleted'] = True | ||
426 | 283 | return adds | ||
427 | 284 | |||
428 | 285 | def delete(self, user, ip_address, cookies={}, commit=True): | ||
429 | 286 | return self.add(0, user, ip_address, cookies, commit) | ||
430 | 287 | |||
431 | 288 | def _get_votes(self, default=None): | ||
432 | 289 | return getattr(self.instance, self.votes_field_name, default) | ||
433 | 290 | |||
434 | 291 | def _set_votes(self, value): | ||
435 | 292 | return setattr(self.instance, self.votes_field_name, value) | ||
436 | 293 | |||
437 | 294 | votes = property(_get_votes, _set_votes) | ||
438 | 295 | |||
439 | 296 | def _get_score(self, default=None): | ||
440 | 297 | return getattr(self.instance, self.score_field_name, default) | ||
441 | 298 | |||
442 | 299 | def _set_score(self, value): | ||
443 | 300 | return setattr(self.instance, self.score_field_name, value) | ||
444 | 301 | |||
445 | 302 | score = property(_get_score, _set_score) | ||
446 | 303 | |||
447 | 304 | def get_content_type(self): | ||
448 | 305 | if self.content_type is None: | ||
449 | 306 | self.content_type = ContentType.objects.get_for_model( | ||
450 | 307 | self.instance) | ||
451 | 308 | return self.content_type | ||
452 | 309 | |||
453 | 310 | def _update(self, commit=False): | ||
454 | 311 | """Forces an update of this rating (useful for when Vote objects are | ||
455 | 312 | removed).""" | ||
456 | 313 | votes = Vote.objects.filter( | ||
457 | 314 | content_type=self.get_content_type(), | ||
458 | 315 | object_id=self.instance.pk, | ||
459 | 316 | key=self.field.key, | ||
460 | 317 | ) | ||
461 | 318 | obj_score = sum([v.score for v in votes]) | ||
462 | 319 | obj_votes = len(votes) | ||
463 | 320 | |||
464 | 321 | score, created = Score.objects.get_or_create( | ||
465 | 322 | content_type=self.get_content_type(), | ||
466 | 323 | object_id=self.instance.pk, | ||
467 | 324 | key=self.field.key, | ||
468 | 325 | defaults=dict( | ||
469 | 326 | score=obj_score, | ||
470 | 327 | votes=obj_votes, | ||
471 | 328 | ) | ||
472 | 329 | ) | ||
473 | 330 | if not created: | ||
474 | 331 | score.score = obj_score | ||
475 | 332 | score.votes = obj_votes | ||
476 | 333 | score.save() | ||
477 | 334 | self.score = obj_score | ||
478 | 335 | self.votes = obj_votes | ||
479 | 336 | if commit: | ||
480 | 337 | self.instance.save() | ||
481 | 338 | |||
482 | 339 | |||
483 | 340 | class RatingCreator(object): | ||
484 | 341 | |||
485 | 342 | def __init__(self, field): | ||
486 | 343 | self.field = field | ||
487 | 344 | self.votes_field_name = '%s_votes' % (self.field.name,) | ||
488 | 345 | self.score_field_name = '%s_score' % (self.field.name,) | ||
489 | 346 | |||
490 | 347 | def __get__(self, instance, type=None): | ||
491 | 348 | if instance is None: | ||
492 | 349 | return self.field | ||
493 | 350 | #raise AttributeError('Can only be accessed via an instance.') | ||
494 | 351 | return RatingManager(instance, self.field) | ||
495 | 352 | |||
496 | 353 | def __set__(self, instance, value): | ||
497 | 354 | if isinstance(value, Rating): | ||
498 | 355 | setattr(instance, self.votes_field_name, value.votes) | ||
499 | 356 | setattr(instance, self.score_field_name, value.score) | ||
500 | 357 | else: | ||
501 | 358 | raise TypeError("%s value must be a Rating instance, not '%r'" % ( | ||
502 | 359 | self.field.name, value)) | ||
503 | 360 | |||
504 | 361 | |||
505 | 362 | class RatingField(IntegerField): | ||
506 | 363 | """A rating field contributes two columns to the model instead of the | ||
507 | 364 | standard single column.""" | ||
508 | 365 | |||
509 | 366 | def __init__(self, *args, **kwargs): | ||
510 | 367 | if 'choices' in kwargs: | ||
511 | 368 | raise TypeError("%s invalid attribute 'choices'" % | ||
512 | 369 | (self.__class__.__name__,)) | ||
513 | 370 | self.can_change_vote = kwargs.pop('can_change_vote', False) | ||
514 | 371 | self.weight = kwargs.pop('weight', 0) | ||
515 | 372 | self.range = kwargs.pop('range', 2) | ||
516 | 373 | self.allow_anonymous = kwargs.pop('allow_anonymous', False) | ||
517 | 374 | self.use_cookies = kwargs.pop('use_cookies', False) | ||
518 | 375 | self.allow_delete = kwargs.pop('allow_delete', False) | ||
519 | 376 | kwargs['editable'] = False | ||
520 | 377 | kwargs['default'] = 0 | ||
521 | 378 | kwargs['blank'] = True | ||
522 | 379 | super(RatingField, self).__init__(*args, **kwargs) | ||
523 | 380 | |||
524 | 381 | def contribute_to_class(self, cls, name): | ||
525 | 382 | self.name = name | ||
526 | 383 | |||
527 | 384 | # Votes tally field | ||
528 | 385 | self.votes_field = PositiveIntegerField( | ||
529 | 386 | editable=False, default=0, blank=True) | ||
530 | 387 | cls.add_to_class('%s_votes' % (self.name,), self.votes_field) | ||
531 | 388 | |||
532 | 389 | # Score sum field | ||
533 | 390 | self.score_field = IntegerField( | ||
534 | 391 | editable=False, default=0, blank=True) | ||
535 | 392 | cls.add_to_class('%s_score' % (self.name,), self.score_field) | ||
536 | 393 | |||
537 | 394 | self.key = md5_hexdigest(self.name) | ||
538 | 395 | |||
539 | 396 | field = RatingCreator(self) | ||
540 | 397 | |||
541 | 398 | if not hasattr(cls, '_djangoratings'): | ||
542 | 399 | cls._djangoratings = [] | ||
543 | 400 | cls._djangoratings.append(self) | ||
544 | 401 | |||
545 | 402 | setattr(cls, name, field) | ||
546 | 403 | |||
547 | 404 | def get_db_prep_save(self, value): | ||
548 | 405 | # XXX: what happens here? | ||
549 | 406 | pass | ||
550 | 407 | |||
551 | 408 | def get_db_prep_lookup(self, lookup_type, value): | ||
552 | 409 | # TODO: hack in support for __score and __votes | ||
553 | 410 | # TODO: order_by on this field should use the weighted algorithm | ||
554 | 411 | raise NotImplementedError(self.get_db_prep_lookup) | ||
555 | 412 | # if lookup_type in ('score', 'votes'): | ||
556 | 413 | # lookup_type = | ||
557 | 414 | # return self.score_field.get_db_prep_lookup() | ||
558 | 415 | if lookup_type == 'exact': | ||
559 | 416 | return [self.get_db_prep_save(value)] | ||
560 | 417 | elif lookup_type == 'in': | ||
561 | 418 | return [self.get_db_prep_save(v) for v in value] | ||
562 | 419 | else: | ||
563 | 420 | return super(RatingField, self).get_db_prep_lookup(lookup_type, value) | ||
564 | 421 | |||
565 | 422 | def formfield(self, **kwargs): | ||
566 | 423 | defaults = {'form_class': forms.RatingField} | ||
567 | 424 | defaults.update(kwargs) | ||
568 | 425 | return super(RatingField, self).formfield(**defaults) | ||
569 | 426 | |||
570 | 427 | # TODO: flatten_data method | ||
571 | 428 | |||
572 | 429 | |||
573 | 430 | class AnonymousRatingField(RatingField): | ||
574 | 431 | |||
575 | 432 | def __init__(self, *args, **kwargs): | ||
576 | 433 | kwargs['allow_anonymous'] = True | ||
577 | 434 | super(AnonymousRatingField, self).__init__(*args, **kwargs) | ||
578 | 435 | 0 | ||
579 | === removed file 'djangoratings/forms.py' | |||
580 | --- djangoratings/forms.py 2016-12-13 18:28:51 +0000 | |||
581 | +++ djangoratings/forms.py 1970-01-01 00:00:00 +0000 | |||
582 | @@ -1,7 +0,0 @@ | |||
583 | 1 | from django import forms | ||
584 | 2 | |||
585 | 3 | __all__ = ('RatingField',) | ||
586 | 4 | |||
587 | 5 | |||
588 | 6 | class RatingField(forms.ChoiceField): | ||
589 | 7 | pass | ||
590 | 8 | 0 | ||
591 | === removed directory 'djangoratings/management' | |||
592 | === removed file 'djangoratings/management/__init__.py' | |||
593 | === removed directory 'djangoratings/management/commands' | |||
594 | === removed file 'djangoratings/management/commands/__init__.py' | |||
595 | === removed file 'djangoratings/management/commands/update_recommendations.py' | |||
596 | --- djangoratings/management/commands/update_recommendations.py 2018-04-05 07:30:42 +0000 | |||
597 | +++ djangoratings/management/commands/update_recommendations.py 1970-01-01 00:00:00 +0000 | |||
598 | @@ -1,9 +0,0 @@ | |||
599 | 1 | from django.core.management.base import BaseCommand, CommandError | ||
600 | 2 | |||
601 | 3 | from djangoratings.models import SimilarUser | ||
602 | 4 | |||
603 | 5 | |||
604 | 6 | class Command(BaseCommand): | ||
605 | 7 | |||
606 | 8 | def handle(self, *args, **options): | ||
607 | 9 | SimilarUser.objects.update_recommendations() | ||
608 | 10 | 0 | ||
609 | === removed file 'djangoratings/managers.py' | |||
610 | --- djangoratings/managers.py 2016-12-13 18:28:51 +0000 | |||
611 | +++ djangoratings/managers.py 1970-01-01 00:00:00 +0000 | |||
612 | @@ -1,124 +0,0 @@ | |||
613 | 1 | from django.db.models import Manager | ||
614 | 2 | from django.db.models.query import QuerySet | ||
615 | 3 | |||
616 | 4 | from django.contrib.contenttypes.models import ContentType | ||
617 | 5 | import itertools | ||
618 | 6 | |||
619 | 7 | |||
620 | 8 | class VoteQuerySet(QuerySet): | ||
621 | 9 | |||
622 | 10 | def delete(self, *args, **kwargs): | ||
623 | 11 | """Handles updating the related `votes` and `score` fields attached to | ||
624 | 12 | the model.""" | ||
625 | 13 | # XXX: circular import | ||
626 | 14 | from fields import RatingField | ||
627 | 15 | |||
628 | 16 | qs = self.distinct().values_list( | ||
629 | 17 | 'content_type', 'object_id').order_by('content_type') | ||
630 | 18 | |||
631 | 19 | to_update = [] | ||
632 | 20 | for content_type, objects in itertools.groupby(qs, key=lambda x: x[0]): | ||
633 | 21 | model_class = ContentType.objects.get( | ||
634 | 22 | pk=content_type).model_class() | ||
635 | 23 | if model_class: | ||
636 | 24 | to_update.extend( | ||
637 | 25 | list(model_class.objects.filter(pk__in=list(objects)[0]))) | ||
638 | 26 | |||
639 | 27 | retval = super(VoteQuerySet, self).delete(*args, **kwargs) | ||
640 | 28 | |||
641 | 29 | # TODO: this could be improved | ||
642 | 30 | for obj in to_update: | ||
643 | 31 | for field in getattr(obj, '_djangoratings', []): | ||
644 | 32 | getattr(obj, field.name)._update(commit=False) | ||
645 | 33 | obj.save() | ||
646 | 34 | |||
647 | 35 | return retval | ||
648 | 36 | |||
649 | 37 | |||
650 | 38 | class VoteManager(Manager): | ||
651 | 39 | |||
652 | 40 | def get_query_set(self): | ||
653 | 41 | return VoteQuerySet(self.model) | ||
654 | 42 | |||
655 | 43 | def get_for_user_in_bulk(self, objects, user): | ||
656 | 44 | objects = list(objects) | ||
657 | 45 | if len(objects) > 0: | ||
658 | 46 | ctype = ContentType.objects.get_for_model(objects[0]) | ||
659 | 47 | votes = list(self.filter(content_type__pk=ctype.id, | ||
660 | 48 | object_id__in=[obj._get_pk_val() | ||
661 | 49 | for obj in objects], | ||
662 | 50 | user__pk=user.id)) | ||
663 | 51 | vote_dict = dict([(vote.object_id, vote) for vote in votes]) | ||
664 | 52 | else: | ||
665 | 53 | vote_dict = {} | ||
666 | 54 | return vote_dict | ||
667 | 55 | |||
668 | 56 | |||
669 | 57 | class SimilarUserManager(Manager): | ||
670 | 58 | |||
671 | 59 | def get_recommendations(self, user, model_class, min_score=1): | ||
672 | 60 | from djangoratings.models import Vote, IgnoredObject | ||
673 | 61 | |||
674 | 62 | content_type = ContentType.objects.get_for_model(model_class) | ||
675 | 63 | |||
676 | 64 | params = dict( | ||
677 | 65 | v=Vote._meta.db_table, | ||
678 | 66 | sm=self.model._meta.db_table, | ||
679 | 67 | m=model_class._meta.db_table, | ||
680 | 68 | io=IgnoredObject._meta.db_table, | ||
681 | 69 | ) | ||
682 | 70 | |||
683 | 71 | objects = model_class._default_manager.extra( | ||
684 | 72 | tables=[params['v']], | ||
685 | 73 | where=[ | ||
686 | 74 | '%(v)s.object_id = %(m)s.id and %(v)s.content_type_id = %%s' % params, | ||
687 | 75 | '%(v)s.user_id IN (select to_user_id from %(sm)s where from_user_id = %%s and exclude = 0)' % params, | ||
688 | 76 | '%(v)s.score >= %%s' % params, | ||
689 | 77 | # Exclude already rated maps | ||
690 | 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, | ||
691 | 79 | # IgnoredObject exclusions | ||
692 | 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, | ||
693 | 81 | ], | ||
694 | 82 | params=[content_type.id, user.id, min_score, user.id, user.id] | ||
695 | 83 | ).distinct() | ||
696 | 84 | |||
697 | 85 | # objects = model_class._default_manager.filter(pk__in=content_type.votes.extra( | ||
698 | 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)], | ||
699 | 87 | # ).filter(score__gte=min_score).exclude( | ||
700 | 88 | # object_id__in=IgnoredObject.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True), | ||
701 | 89 | # ).exclude( | ||
702 | 90 | # object_id__in=Vote.objects.filter(content_type=content_type, user=user).values_list('object_id', flat=True) | ||
703 | 91 | # ).distinct().values_list('object_id', flat=True)) | ||
704 | 92 | |||
705 | 93 | return objects | ||
706 | 94 | |||
707 | 95 | def update_recommendations(self): | ||
708 | 96 | # TODO: this is mysql only atm | ||
709 | 97 | # TODO: this doesnt handle scores that have multiple values (e.g. 10 points, 5 stars) | ||
710 | 98 | # due to it calling an agreement as score = score. We need to loop each rating instance | ||
711 | 99 | # and express the condition based on the range. | ||
712 | 100 | from djangoratings.models import Vote | ||
713 | 101 | from django.db import connection | ||
714 | 102 | cursor = connection.cursor() | ||
715 | 103 | cursor.execute('begin') | ||
716 | 104 | cursor.execute('truncate table %s' % (self.model._meta.db_table,)) | ||
717 | 105 | cursor.execute("""insert into %(t1)s | ||
718 | 106 | (to_user_id, from_user_id, agrees, disagrees, exclude) | ||
719 | 107 | select v1.user_id, v2.user_id, | ||
720 | 108 | sum(if(v2.score = v1.score, 1, 0)) as agrees, | ||
721 | 109 | sum(if(v2.score != v1.score, 1, 0)) as disagrees, 0 | ||
722 | 110 | from %(t2)s as v1 | ||
723 | 111 | inner join %(t2)s as v2 | ||
724 | 112 | on v1.user_id != v2.user_id | ||
725 | 113 | and v1.object_id = v2.object_id | ||
726 | 114 | and v1.content_type_id = v2.content_type_id | ||
727 | 115 | where v1.user_id is not null | ||
728 | 116 | and v2.user_id is not null | ||
729 | 117 | group by v1.user_id, v2.user_id | ||
730 | 118 | having agrees / (disagrees + 0.0001) > 3 | ||
731 | 119 | on duplicate key update agrees = values(agrees), disagrees = values(disagrees);""" % dict( | ||
732 | 120 | t1=self.model._meta.db_table, | ||
733 | 121 | t2=Vote._meta.db_table, | ||
734 | 122 | )) | ||
735 | 123 | cursor.execute('commit') | ||
736 | 124 | cursor.close() | ||
737 | 125 | 0 | ||
738 | === removed directory 'djangoratings/migrations' | |||
739 | === removed file 'djangoratings/migrations/0001_initial.py' | |||
740 | --- djangoratings/migrations/0001_initial.py 2016-12-13 18:28:51 +0000 | |||
741 | +++ djangoratings/migrations/0001_initial.py 1970-01-01 00:00:00 +0000 | |||
742 | @@ -1,90 +0,0 @@ | |||
743 | 1 | # -*- coding: utf-8 -*- | ||
744 | 2 | from __future__ import unicode_literals | ||
745 | 3 | |||
746 | 4 | from django.db import models, migrations | ||
747 | 5 | import django.utils.timezone | ||
748 | 6 | from django.conf import settings | ||
749 | 7 | |||
750 | 8 | |||
751 | 9 | class Migration(migrations.Migration): | ||
752 | 10 | |||
753 | 11 | dependencies = [ | ||
754 | 12 | ('contenttypes', '0002_remove_content_type_name'), | ||
755 | 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
756 | 14 | ] | ||
757 | 15 | |||
758 | 16 | operations = [ | ||
759 | 17 | migrations.CreateModel( | ||
760 | 18 | name='IgnoredObject', | ||
761 | 19 | fields=[ | ||
762 | 20 | ('id', models.AutoField(verbose_name='ID', | ||
763 | 21 | serialize=False, auto_created=True, primary_key=True)), | ||
764 | 22 | ('object_id', models.PositiveIntegerField()), | ||
765 | 23 | ('content_type', models.ForeignKey(to='contenttypes.ContentType')), | ||
766 | 24 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | ||
767 | 25 | ], | ||
768 | 26 | ), | ||
769 | 27 | migrations.CreateModel( | ||
770 | 28 | name='Score', | ||
771 | 29 | fields=[ | ||
772 | 30 | ('id', models.AutoField(verbose_name='ID', | ||
773 | 31 | serialize=False, auto_created=True, primary_key=True)), | ||
774 | 32 | ('object_id', models.PositiveIntegerField()), | ||
775 | 33 | ('key', models.CharField(max_length=32)), | ||
776 | 34 | ('score', models.IntegerField()), | ||
777 | 35 | ('votes', models.PositiveIntegerField()), | ||
778 | 36 | ('content_type', models.ForeignKey(to='contenttypes.ContentType')), | ||
779 | 37 | ], | ||
780 | 38 | ), | ||
781 | 39 | migrations.CreateModel( | ||
782 | 40 | name='SimilarUser', | ||
783 | 41 | fields=[ | ||
784 | 42 | ('id', models.AutoField(verbose_name='ID', | ||
785 | 43 | serialize=False, auto_created=True, primary_key=True)), | ||
786 | 44 | ('agrees', models.PositiveIntegerField(default=0)), | ||
787 | 45 | ('disagrees', models.PositiveIntegerField(default=0)), | ||
788 | 46 | ('exclude', models.BooleanField(default=False)), | ||
789 | 47 | ('from_user', models.ForeignKey( | ||
790 | 48 | related_name='similar_users', to=settings.AUTH_USER_MODEL)), | ||
791 | 49 | ('to_user', models.ForeignKey( | ||
792 | 50 | related_name='similar_users_from', to=settings.AUTH_USER_MODEL)), | ||
793 | 51 | ], | ||
794 | 52 | ), | ||
795 | 53 | migrations.CreateModel( | ||
796 | 54 | name='Vote', | ||
797 | 55 | fields=[ | ||
798 | 56 | ('id', models.AutoField(verbose_name='ID', | ||
799 | 57 | serialize=False, auto_created=True, primary_key=True)), | ||
800 | 58 | ('object_id', models.PositiveIntegerField()), | ||
801 | 59 | ('key', models.CharField(max_length=32)), | ||
802 | 60 | ('score', models.IntegerField()), | ||
803 | 61 | ('ip_address', models.GenericIPAddressField()), | ||
804 | 62 | ('cookie', models.CharField(max_length=32, null=True, blank=True)), | ||
805 | 63 | ('date_added', models.DateTimeField( | ||
806 | 64 | default=django.utils.timezone.now, editable=False)), | ||
807 | 65 | ('date_changed', models.DateTimeField( | ||
808 | 66 | default=django.utils.timezone.now, editable=False)), | ||
809 | 67 | ('content_type', models.ForeignKey( | ||
810 | 68 | related_name='votes', to='contenttypes.ContentType')), | ||
811 | 69 | ('user', models.ForeignKey(related_name='votes', | ||
812 | 70 | blank=True, to=settings.AUTH_USER_MODEL, null=True)), | ||
813 | 71 | ], | ||
814 | 72 | ), | ||
815 | 73 | migrations.AlterUniqueTogether( | ||
816 | 74 | name='vote', | ||
817 | 75 | unique_together=set( | ||
818 | 76 | [('content_type', 'object_id', 'key', 'user', 'ip_address', 'cookie')]), | ||
819 | 77 | ), | ||
820 | 78 | migrations.AlterUniqueTogether( | ||
821 | 79 | name='similaruser', | ||
822 | 80 | unique_together=set([('from_user', 'to_user')]), | ||
823 | 81 | ), | ||
824 | 82 | migrations.AlterUniqueTogether( | ||
825 | 83 | name='score', | ||
826 | 84 | unique_together=set([('content_type', 'object_id', 'key')]), | ||
827 | 85 | ), | ||
828 | 86 | migrations.AlterUniqueTogether( | ||
829 | 87 | name='ignoredobject', | ||
830 | 88 | unique_together=set([('content_type', 'object_id')]), | ||
831 | 89 | ), | ||
832 | 90 | ] | ||
833 | 91 | 0 | ||
834 | === removed file 'djangoratings/migrations/__init__.py' | |||
835 | === removed file 'djangoratings/models.py' | |||
836 | --- djangoratings/models.py 2016-12-13 18:28:51 +0000 | |||
837 | +++ djangoratings/models.py 1970-01-01 00:00:00 +0000 | |||
838 | @@ -1,98 +0,0 @@ | |||
839 | 1 | from datetime import datetime | ||
840 | 2 | |||
841 | 3 | from django.db import models | ||
842 | 4 | from django.contrib.contenttypes.models import ContentType | ||
843 | 5 | from django.contrib.contenttypes.fields import GenericForeignKey | ||
844 | 6 | from django.contrib.auth.models import User | ||
845 | 7 | |||
846 | 8 | try: | ||
847 | 9 | from django.utils.timezone import now | ||
848 | 10 | except ImportError: | ||
849 | 11 | now = datetime.now | ||
850 | 12 | |||
851 | 13 | from managers import VoteManager, SimilarUserManager | ||
852 | 14 | |||
853 | 15 | |||
854 | 16 | class Vote(models.Model): | ||
855 | 17 | content_type = models.ForeignKey(ContentType, related_name='votes') | ||
856 | 18 | object_id = models.PositiveIntegerField() | ||
857 | 19 | key = models.CharField(max_length=32) | ||
858 | 20 | score = models.IntegerField() | ||
859 | 21 | user = models.ForeignKey(User, blank=True, null=True, related_name='votes') | ||
860 | 22 | ip_address = models.GenericIPAddressField() | ||
861 | 23 | cookie = models.CharField(max_length=32, blank=True, null=True) | ||
862 | 24 | date_added = models.DateTimeField(default=now, editable=False) | ||
863 | 25 | date_changed = models.DateTimeField(default=now, editable=False) | ||
864 | 26 | |||
865 | 27 | objects = VoteManager() | ||
866 | 28 | |||
867 | 29 | content_object = GenericForeignKey() | ||
868 | 30 | |||
869 | 31 | class Meta: | ||
870 | 32 | unique_together = (('content_type', 'object_id', | ||
871 | 33 | 'key', 'user', 'ip_address', 'cookie')) | ||
872 | 34 | |||
873 | 35 | def __unicode__(self): | ||
874 | 36 | return u"%s voted %s on %s" % (self.user_display, self.score, self.content_object) | ||
875 | 37 | |||
876 | 38 | def save(self, *args, **kwargs): | ||
877 | 39 | self.date_changed = now() | ||
878 | 40 | super(Vote, self).save(*args, **kwargs) | ||
879 | 41 | |||
880 | 42 | def user_display(self): | ||
881 | 43 | if self.user: | ||
882 | 44 | return '%s (%s)' % (self.user.username, self.ip_address) | ||
883 | 45 | return self.ip_address | ||
884 | 46 | user_display = property(user_display) | ||
885 | 47 | |||
886 | 48 | def partial_ip_address(self): | ||
887 | 49 | ip = self.ip_address.split('.') | ||
888 | 50 | ip[-1] = 'xxx' | ||
889 | 51 | return '.'.join(ip) | ||
890 | 52 | partial_ip_address = property(partial_ip_address) | ||
891 | 53 | |||
892 | 54 | |||
893 | 55 | class Score(models.Model): | ||
894 | 56 | content_type = models.ForeignKey(ContentType) | ||
895 | 57 | object_id = models.PositiveIntegerField() | ||
896 | 58 | key = models.CharField(max_length=32) | ||
897 | 59 | score = models.IntegerField() | ||
898 | 60 | votes = models.PositiveIntegerField() | ||
899 | 61 | |||
900 | 62 | content_object = GenericForeignKey() | ||
901 | 63 | |||
902 | 64 | class Meta: | ||
903 | 65 | unique_together = (('content_type', 'object_id', 'key'),) | ||
904 | 66 | |||
905 | 67 | def __unicode__(self): | ||
906 | 68 | return u"%s scored %s with %s votes" % (self.content_object, self.score, self.votes) | ||
907 | 69 | |||
908 | 70 | |||
909 | 71 | class SimilarUser(models.Model): | ||
910 | 72 | from_user = models.ForeignKey(User, related_name='similar_users') | ||
911 | 73 | to_user = models.ForeignKey(User, related_name='similar_users_from') | ||
912 | 74 | agrees = models.PositiveIntegerField(default=0) | ||
913 | 75 | disagrees = models.PositiveIntegerField(default=0) | ||
914 | 76 | exclude = models.BooleanField(default=False) | ||
915 | 77 | |||
916 | 78 | objects = SimilarUserManager() | ||
917 | 79 | |||
918 | 80 | class Meta: | ||
919 | 81 | unique_together = (('from_user', 'to_user'),) | ||
920 | 82 | |||
921 | 83 | def __unicode__(self): | ||
922 | 84 | print u"%s %s similar to %s" % (self.from_user, self.exclude and 'is not' or 'is', self.to_user) | ||
923 | 85 | |||
924 | 86 | |||
925 | 87 | class IgnoredObject(models.Model): | ||
926 | 88 | user = models.ForeignKey(User) | ||
927 | 89 | content_type = models.ForeignKey(ContentType) | ||
928 | 90 | object_id = models.PositiveIntegerField() | ||
929 | 91 | |||
930 | 92 | content_object = GenericForeignKey() | ||
931 | 93 | |||
932 | 94 | class Meta: | ||
933 | 95 | unique_together = (('content_type', 'object_id'),) | ||
934 | 96 | |||
935 | 97 | def __unicode__(self): | ||
936 | 98 | return self.content_object | ||
937 | 99 | 0 | ||
938 | === removed file 'djangoratings/runtests.py' | |||
939 | --- djangoratings/runtests.py 2016-12-13 18:28:51 +0000 | |||
940 | +++ djangoratings/runtests.py 1970-01-01 00:00:00 +0000 | |||
941 | @@ -1,31 +0,0 @@ | |||
942 | 1 | #!/usr/bin/env python | ||
943 | 2 | import sys | ||
944 | 3 | |||
945 | 4 | from os.path import dirname, abspath | ||
946 | 5 | |||
947 | 6 | from django.conf import settings | ||
948 | 7 | |||
949 | 8 | if not settings.configured: | ||
950 | 9 | settings.configure( | ||
951 | 10 | DATABASE_ENGINE='sqlite3', | ||
952 | 11 | INSTALLED_APPS=[ | ||
953 | 12 | 'django.contrib.auth', | ||
954 | 13 | 'django.contrib.contenttypes', | ||
955 | 14 | 'djangoratings', | ||
956 | 15 | ] | ||
957 | 16 | ) | ||
958 | 17 | |||
959 | 18 | from django.test.simple import run_tests | ||
960 | 19 | |||
961 | 20 | |||
962 | 21 | def runtests(*test_args): | ||
963 | 22 | if not test_args: | ||
964 | 23 | test_args = ['djangoratings'] | ||
965 | 24 | parent = dirname(abspath(__file__)) | ||
966 | 25 | sys.path.insert(0, parent) | ||
967 | 26 | failures = run_tests(test_args, verbosity=1, interactive=True) | ||
968 | 27 | sys.exit(failures) | ||
969 | 28 | |||
970 | 29 | |||
971 | 30 | if __name__ == '__main__': | ||
972 | 31 | runtests(*sys.argv[1:]) | ||
973 | 32 | 0 | ||
974 | === removed directory 'djangoratings/templatetags' | |||
975 | === removed file 'djangoratings/templatetags/__init__.py' | |||
976 | === removed file 'djangoratings/templatetags/ratings_old.py' | |||
977 | --- djangoratings/templatetags/ratings_old.py 2018-11-18 16:16:04 +0000 | |||
978 | +++ djangoratings/templatetags/ratings_old.py 1970-01-01 00:00:00 +0000 | |||
979 | @@ -1,101 +0,0 @@ | |||
980 | 1 | """Template tags for Django.""" | ||
981 | 2 | # TODO: add in Jinja tags if Coffin is available | ||
982 | 3 | |||
983 | 4 | from django import template | ||
984 | 5 | from django.contrib.contenttypes.models import ContentType | ||
985 | 6 | from django.db.models import ObjectDoesNotExist | ||
986 | 7 | |||
987 | 8 | from djangoratings.models import Vote | ||
988 | 9 | from wl_utils import get_real_ip | ||
989 | 10 | |||
990 | 11 | register = template.Library() | ||
991 | 12 | |||
992 | 13 | |||
993 | 14 | class RatingByRequestNode(template.Node): | ||
994 | 15 | |||
995 | 16 | def __init__(self, request, obj, context_var): | ||
996 | 17 | self.request = request | ||
997 | 18 | self.obj, self.field_name = obj.split('.') | ||
998 | 19 | self.context_var = context_var | ||
999 | 20 | |||
1000 | 21 | def render(self, context): | ||
1001 | 22 | try: | ||
1002 | 23 | request = django.template.Variable(self.request).resolve(context) | ||
1003 | 24 | obj = django.template.Variable(self.obj).resolve(context) | ||
1004 | 25 | field = getattr(obj, self.field_name) | ||
1005 | 26 | except (template.VariableDoesNotExist, AttributeError): | ||
1006 | 27 | return '' | ||
1007 | 28 | try: | ||
1008 | 29 | vote = field.get_rating_for_user( | ||
1009 | 30 | request.user, get_real_ip(request), request.COOKIES) | ||
1010 | 31 | context[self.context_var] = vote | ||
1011 | 32 | except ObjectDoesNotExist: | ||
1012 | 33 | context[self.context_var] = 0 | ||
1013 | 34 | return '' | ||
1014 | 35 | |||
1015 | 36 | |||
1016 | 37 | def do_rating_by_request(parser, token): | ||
1017 | 38 | """ | ||
1018 | 39 | Retrieves the ``Vote`` cast by a user on a particular object and | ||
1019 | 40 | stores it in a context variable. If the user has not voted, the | ||
1020 | 41 | context variable will be 0. | ||
1021 | 42 | |||
1022 | 43 | Example usage:: | ||
1023 | 44 | |||
1024 | 45 | {% rating_by_request request on instance as vote %} | ||
1025 | 46 | """ | ||
1026 | 47 | |||
1027 | 48 | bits = token.contents.split() | ||
1028 | 49 | if len(bits) != 6: | ||
1029 | 50 | raise template.TemplateSyntaxError( | ||
1030 | 51 | "'%s' tag takes exactly five arguments" % bits[0]) | ||
1031 | 52 | if bits[2] != 'on': | ||
1032 | 53 | raise template.TemplateSyntaxError( | ||
1033 | 54 | "second argument to '%s' tag must be 'on'" % bits[0]) | ||
1034 | 55 | if bits[4] != 'as': | ||
1035 | 56 | raise template.TemplateSyntaxError( | ||
1036 | 57 | "fourth argument to '%s' tag must be 'as'" % bits[0]) | ||
1037 | 58 | return RatingByRequestNode(bits[1], bits[3], bits[5]) | ||
1038 | 59 | register.tag('rating_by_request', do_rating_by_request) | ||
1039 | 60 | |||
1040 | 61 | |||
1041 | 62 | class RatingByUserNode(RatingByRequestNode): | ||
1042 | 63 | |||
1043 | 64 | def render(self, context): | ||
1044 | 65 | try: | ||
1045 | 66 | user = django.template.Variable(self.request).resolve(context) | ||
1046 | 67 | obj = django.template.Variable(self.obj).resolve(context) | ||
1047 | 68 | field = getattr(obj, self.field_name) | ||
1048 | 69 | except template.VariableDoesNotExist: | ||
1049 | 70 | return '' | ||
1050 | 71 | try: | ||
1051 | 72 | vote = field.get_rating_for_user(user) | ||
1052 | 73 | context[self.context_var] = vote | ||
1053 | 74 | except ObjectDoesNotExist: | ||
1054 | 75 | context[self.context_var] = 0 | ||
1055 | 76 | return '' | ||
1056 | 77 | |||
1057 | 78 | |||
1058 | 79 | def do_rating_by_user(parser, token): | ||
1059 | 80 | """ | ||
1060 | 81 | Retrieves the ``Vote`` cast by a user on a particular object and | ||
1061 | 82 | stores it in a context variable. If the user has not voted, the | ||
1062 | 83 | context variable will be 0. | ||
1063 | 84 | |||
1064 | 85 | Example usage:: | ||
1065 | 86 | |||
1066 | 87 | {% rating_by_user user on instance as vote %} | ||
1067 | 88 | """ | ||
1068 | 89 | |||
1069 | 90 | bits = token.contents.split() | ||
1070 | 91 | if len(bits) != 6: | ||
1071 | 92 | raise template.TemplateSyntaxError( | ||
1072 | 93 | "'%s' tag takes exactly five arguments" % bits[0]) | ||
1073 | 94 | if bits[2] != 'on': | ||
1074 | 95 | raise template.TemplateSyntaxError( | ||
1075 | 96 | "second argument to '%s' tag must be 'on'" % bits[0]) | ||
1076 | 97 | if bits[4] != 'as': | ||
1077 | 98 | raise template.TemplateSyntaxError( | ||
1078 | 99 | "fourth argument to '%s' tag must be 'as'" % bits[0]) | ||
1079 | 100 | return RatingByUserNode(bits[1], bits[3], bits[5]) | ||
1080 | 101 | register.tag('rating_by_user', do_rating_by_user) | ||
1081 | 102 | 0 | ||
1082 | === removed file 'djangoratings/tests.py' | |||
1083 | --- djangoratings/tests.py 2016-12-13 18:28:51 +0000 | |||
1084 | +++ djangoratings/tests.py 1970-01-01 00:00:00 +0000 | |||
1085 | @@ -1,184 +0,0 @@ | |||
1086 | 1 | import unittest | ||
1087 | 2 | import random | ||
1088 | 3 | |||
1089 | 4 | from django.db import models | ||
1090 | 5 | from django.contrib.auth.models import User | ||
1091 | 6 | from django.contrib.contenttypes.models import ContentType | ||
1092 | 7 | from django.conf import settings | ||
1093 | 8 | |||
1094 | 9 | from exceptions import * | ||
1095 | 10 | from models import Vote, SimilarUser, IgnoredObject | ||
1096 | 11 | from fields import AnonymousRatingField, RatingField | ||
1097 | 12 | |||
1098 | 13 | settings.RATINGS_VOTES_PER_IP = 1 | ||
1099 | 14 | |||
1100 | 15 | |||
1101 | 16 | class RatingTestModel(models.Model): | ||
1102 | 17 | rating = AnonymousRatingField(range=2, can_change_vote=True) | ||
1103 | 18 | rating2 = RatingField(range=2, can_change_vote=False) | ||
1104 | 19 | |||
1105 | 20 | def __unicode__(self): | ||
1106 | 21 | return unicode(self.pk) | ||
1107 | 22 | |||
1108 | 23 | |||
1109 | 24 | class RatingTestCase(unittest.TestCase): | ||
1110 | 25 | |||
1111 | 26 | def testRatings(self): | ||
1112 | 27 | instance = RatingTestModel.objects.create() | ||
1113 | 28 | |||
1114 | 29 | # Test adding votes | ||
1115 | 30 | instance.rating.add(score=1, user=None, ip_address='127.0.0.1') | ||
1116 | 31 | self.assertEquals(instance.rating.score, 1) | ||
1117 | 32 | self.assertEquals(instance.rating.votes, 1) | ||
1118 | 33 | |||
1119 | 34 | # Test adding votes | ||
1120 | 35 | instance.rating.add(score=2, user=None, ip_address='127.0.0.2') | ||
1121 | 36 | self.assertEquals(instance.rating.score, 3) | ||
1122 | 37 | self.assertEquals(instance.rating.votes, 2) | ||
1123 | 38 | |||
1124 | 39 | # Test changing of votes | ||
1125 | 40 | instance.rating.add(score=2, user=None, ip_address='127.0.0.1') | ||
1126 | 41 | self.assertEquals(instance.rating.score, 4) | ||
1127 | 42 | self.assertEquals(instance.rating.votes, 2) | ||
1128 | 43 | |||
1129 | 44 | # Test users | ||
1130 | 45 | user = User.objects.create(username=str(random.randint(0, 100000000))) | ||
1131 | 46 | user2 = User.objects.create(username=str(random.randint(0, 100000000))) | ||
1132 | 47 | |||
1133 | 48 | instance.rating.add(score=2, user=user, ip_address='127.0.0.3') | ||
1134 | 49 | self.assertEquals(instance.rating.score, 6) | ||
1135 | 50 | self.assertEquals(instance.rating.votes, 3) | ||
1136 | 51 | |||
1137 | 52 | instance.rating2.add(score=2, user=user, ip_address='127.0.0.3') | ||
1138 | 53 | self.assertEquals(instance.rating2.score, 2) | ||
1139 | 54 | self.assertEquals(instance.rating2.votes, 1) | ||
1140 | 55 | |||
1141 | 56 | self.assertRaises(IPLimitReached, instance.rating2.add, | ||
1142 | 57 | score=2, user=user2, ip_address='127.0.0.3') | ||
1143 | 58 | |||
1144 | 59 | # Test deletion hooks | ||
1145 | 60 | Vote.objects.filter(ip_address='127.0.0.3').delete() | ||
1146 | 61 | |||
1147 | 62 | instance = RatingTestModel.objects.get(pk=instance.pk) | ||
1148 | 63 | |||
1149 | 64 | self.assertEquals(instance.rating.score, 4) | ||
1150 | 65 | self.assertEquals(instance.rating.votes, 2) | ||
1151 | 66 | self.assertEquals(instance.rating2.score, 0) | ||
1152 | 67 | self.assertEquals(instance.rating2.votes, 0) | ||
1153 | 68 | |||
1154 | 69 | |||
1155 | 70 | class RecommendationsTestCase(unittest.TestCase): | ||
1156 | 71 | |||
1157 | 72 | def setUp(self): | ||
1158 | 73 | self.instance = RatingTestModel.objects.create() | ||
1159 | 74 | self.instance2 = RatingTestModel.objects.create() | ||
1160 | 75 | self.instance3 = RatingTestModel.objects.create() | ||
1161 | 76 | self.instance4 = RatingTestModel.objects.create() | ||
1162 | 77 | self.instance5 = RatingTestModel.objects.create() | ||
1163 | 78 | |||
1164 | 79 | # Test users | ||
1165 | 80 | self.user = User.objects.create( | ||
1166 | 81 | username=str(random.randint(0, 100000000))) | ||
1167 | 82 | self.user2 = User.objects.create( | ||
1168 | 83 | username=str(random.randint(0, 100000000))) | ||
1169 | 84 | |||
1170 | 85 | def testExclusions(self): | ||
1171 | 86 | Vote.objects.all().delete() | ||
1172 | 87 | |||
1173 | 88 | self.instance.rating.add( | ||
1174 | 89 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1175 | 90 | self.instance2.rating.add( | ||
1176 | 91 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1177 | 92 | self.instance3.rating.add( | ||
1178 | 93 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1179 | 94 | self.instance4.rating.add( | ||
1180 | 95 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1181 | 96 | self.instance5.rating.add( | ||
1182 | 97 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1183 | 98 | self.instance.rating.add( | ||
1184 | 99 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1185 | 100 | |||
1186 | 101 | # we should only need to call this once | ||
1187 | 102 | SimilarUser.objects.update_recommendations() | ||
1188 | 103 | |||
1189 | 104 | self.assertEquals(SimilarUser.objects.count(), 2) | ||
1190 | 105 | |||
1191 | 106 | recs = list(SimilarUser.objects.get_recommendations( | ||
1192 | 107 | self.user2, RatingTestModel)) | ||
1193 | 108 | self.assertEquals(len(recs), 4) | ||
1194 | 109 | |||
1195 | 110 | ct = ContentType.objects.get_for_model(RatingTestModel) | ||
1196 | 111 | |||
1197 | 112 | IgnoredObject.objects.create( | ||
1198 | 113 | user=self.user2, content_type=ct, object_id=self.instance2.pk) | ||
1199 | 114 | |||
1200 | 115 | recs = list(SimilarUser.objects.get_recommendations( | ||
1201 | 116 | self.user2, RatingTestModel)) | ||
1202 | 117 | self.assertEquals(len(recs), 3) | ||
1203 | 118 | |||
1204 | 119 | IgnoredObject.objects.create( | ||
1205 | 120 | user=self.user2, content_type=ct, object_id=self.instance3.pk) | ||
1206 | 121 | IgnoredObject.objects.create( | ||
1207 | 122 | user=self.user2, content_type=ct, object_id=self.instance4.pk) | ||
1208 | 123 | |||
1209 | 124 | recs = list(SimilarUser.objects.get_recommendations( | ||
1210 | 125 | self.user2, RatingTestModel)) | ||
1211 | 126 | self.assertEquals(len(recs), 1) | ||
1212 | 127 | self.assertEquals(recs, [self.instance5]) | ||
1213 | 128 | |||
1214 | 129 | self.instance5.rating.add( | ||
1215 | 130 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1216 | 131 | recs = list(SimilarUser.objects.get_recommendations( | ||
1217 | 132 | self.user2, RatingTestModel)) | ||
1218 | 133 | self.assertEquals(len(recs), 0) | ||
1219 | 134 | |||
1220 | 135 | def testSimilarUsers(self): | ||
1221 | 136 | Vote.objects.all().delete() | ||
1222 | 137 | |||
1223 | 138 | self.instance.rating.add( | ||
1224 | 139 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1225 | 140 | self.instance2.rating.add( | ||
1226 | 141 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1227 | 142 | self.instance3.rating.add( | ||
1228 | 143 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1229 | 144 | self.instance4.rating.add( | ||
1230 | 145 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1231 | 146 | self.instance5.rating.add( | ||
1232 | 147 | score=1, user=self.user, ip_address='127.0.0.1') | ||
1233 | 148 | self.instance.rating.add( | ||
1234 | 149 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1235 | 150 | self.instance2.rating.add( | ||
1236 | 151 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1237 | 152 | self.instance3.rating.add( | ||
1238 | 153 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1239 | 154 | |||
1240 | 155 | SimilarUser.objects.update_recommendations() | ||
1241 | 156 | |||
1242 | 157 | self.assertEquals(SimilarUser.objects.count(), 2) | ||
1243 | 158 | |||
1244 | 159 | recs = list(SimilarUser.objects.get_recommendations( | ||
1245 | 160 | self.user2, RatingTestModel)) | ||
1246 | 161 | self.assertEquals(len(recs), 2) | ||
1247 | 162 | |||
1248 | 163 | self.instance4.rating.add( | ||
1249 | 164 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1250 | 165 | |||
1251 | 166 | SimilarUser.objects.update_recommendations() | ||
1252 | 167 | |||
1253 | 168 | self.assertEquals(SimilarUser.objects.count(), 2) | ||
1254 | 169 | |||
1255 | 170 | recs = list(SimilarUser.objects.get_recommendations( | ||
1256 | 171 | self.user2, RatingTestModel)) | ||
1257 | 172 | self.assertEquals(len(recs), 1) | ||
1258 | 173 | self.assertEquals(recs, [self.instance5]) | ||
1259 | 174 | |||
1260 | 175 | self.instance5.rating.add( | ||
1261 | 176 | score=1, user=self.user2, ip_address='127.0.0.2') | ||
1262 | 177 | |||
1263 | 178 | SimilarUser.objects.update_recommendations() | ||
1264 | 179 | |||
1265 | 180 | self.assertEquals(SimilarUser.objects.count(), 2) | ||
1266 | 181 | |||
1267 | 182 | recs = list(SimilarUser.objects.get_recommendations( | ||
1268 | 183 | self.user2, RatingTestModel)) | ||
1269 | 184 | self.assertEquals(len(recs), 0) | ||
1270 | 185 | 0 | ||
1271 | === removed file 'djangoratings/views.py' | |||
1272 | --- djangoratings/views.py 2016-12-13 18:28:51 +0000 | |||
1273 | +++ djangoratings/views.py 1970-01-01 00:00:00 +0000 | |||
1274 | @@ -1,138 +0,0 @@ | |||
1275 | 1 | from django.contrib.contenttypes.models import ContentType | ||
1276 | 2 | from django.core.exceptions import ObjectDoesNotExist | ||
1277 | 3 | from django.http import HttpResponse, Http404 | ||
1278 | 4 | |||
1279 | 5 | from exceptions import * | ||
1280 | 6 | from django.conf import settings | ||
1281 | 7 | from default_settings import RATINGS_VOTES_PER_IP | ||
1282 | 8 | from wl_utils import get_real_ip | ||
1283 | 9 | |||
1284 | 10 | |||
1285 | 11 | class AddRatingView(object): | ||
1286 | 12 | |||
1287 | 13 | def __call__(self, request, content_type_id, object_id, field_name, score): | ||
1288 | 14 | """__call__(request, content_type_id, object_id, field_name, score) | ||
1289 | 15 | |||
1290 | 16 | Adds a vote to the specified model field. | ||
1291 | 17 | |||
1292 | 18 | """ | ||
1293 | 19 | |||
1294 | 20 | try: | ||
1295 | 21 | instance = self.get_instance(content_type_id, object_id) | ||
1296 | 22 | except ObjectDoesNotExist: | ||
1297 | 23 | raise Http404('Object does not exist') | ||
1298 | 24 | |||
1299 | 25 | context = self.get_context(request) | ||
1300 | 26 | context['instance'] = instance | ||
1301 | 27 | |||
1302 | 28 | try: | ||
1303 | 29 | field = getattr(instance, field_name) | ||
1304 | 30 | except AttributeError: | ||
1305 | 31 | return self.invalid_field_response(request, context) | ||
1306 | 32 | |||
1307 | 33 | context.update({ | ||
1308 | 34 | 'field': field, | ||
1309 | 35 | 'score': score, | ||
1310 | 36 | }) | ||
1311 | 37 | |||
1312 | 38 | had_voted = bool(field.get_rating_for_user( | ||
1313 | 39 | request.user, get_real_ip(request), request.COOKIES)) | ||
1314 | 40 | |||
1315 | 41 | context['had_voted'] = had_voted | ||
1316 | 42 | |||
1317 | 43 | try: | ||
1318 | 44 | adds = field.add(score, request.user, | ||
1319 | 45 | get_real_ip(request), request.COOKIES) | ||
1320 | 46 | except IPLimitReached: | ||
1321 | 47 | return self.too_many_votes_from_ip_response(request, context) | ||
1322 | 48 | except AuthRequired: | ||
1323 | 49 | return self.authentication_required_response(request, context) | ||
1324 | 50 | except InvalidRating: | ||
1325 | 51 | return self.invalid_rating_response(request, context) | ||
1326 | 52 | except CannotChangeVote: | ||
1327 | 53 | return self.cannot_change_vote_response(request, context) | ||
1328 | 54 | except CannotDeleteVote: | ||
1329 | 55 | return self.cannot_delete_vote_response(request, context) | ||
1330 | 56 | if had_voted: | ||
1331 | 57 | return self.rating_changed_response(request, context, adds) | ||
1332 | 58 | return self.rating_added_response(request, context, adds) | ||
1333 | 59 | |||
1334 | 60 | def get_context(self, request, context={}): | ||
1335 | 61 | return context | ||
1336 | 62 | |||
1337 | 63 | def render_to_response(self, template, context, request): | ||
1338 | 64 | raise NotImplementedError | ||
1339 | 65 | |||
1340 | 66 | def too_many_votes_from_ip_response(self, request, context): | ||
1341 | 67 | response = HttpResponse( | ||
1342 | 68 | 'Too many votes from this IP address for this object.') | ||
1343 | 69 | return response | ||
1344 | 70 | |||
1345 | 71 | def rating_changed_response(self, request, context, adds={}): | ||
1346 | 72 | response = HttpResponse('Vote changed.') | ||
1347 | 73 | if 'cookie' in adds: | ||
1348 | 74 | cookie_name, cookie = adds['cookie_name'], adds['cookie'] | ||
1349 | 75 | if 'deleted' in adds: | ||
1350 | 76 | response.delete_cookie(cookie_name) | ||
1351 | 77 | else: | ||
1352 | 78 | # TODO: move cookie max_age to settings | ||
1353 | 79 | response.set_cookie(cookie_name, cookie, 31536000, path='/') | ||
1354 | 80 | return response | ||
1355 | 81 | |||
1356 | 82 | def rating_added_response(self, request, context, adds={}): | ||
1357 | 83 | response = HttpResponse('Vote recorded.') | ||
1358 | 84 | if 'cookie' in adds: | ||
1359 | 85 | cookie_name, cookie = adds['cookie_name'], adds['cookie'] | ||
1360 | 86 | if 'deleted' in adds: | ||
1361 | 87 | response.delete_cookie(cookie_name) | ||
1362 | 88 | else: | ||
1363 | 89 | # TODO: move cookie max_age to settings | ||
1364 | 90 | response.set_cookie(cookie_name, cookie, 31536000, path='/') | ||
1365 | 91 | return response | ||
1366 | 92 | |||
1367 | 93 | def authentication_required_response(self, request, context): | ||
1368 | 94 | response = HttpResponse('You must be logged in to vote.') | ||
1369 | 95 | response.status_code = 403 | ||
1370 | 96 | return response | ||
1371 | 97 | |||
1372 | 98 | def cannot_change_vote_response(self, request, context): | ||
1373 | 99 | response = HttpResponse('You have already voted.') | ||
1374 | 100 | response.status_code = 403 | ||
1375 | 101 | return response | ||
1376 | 102 | |||
1377 | 103 | def cannot_delete_vote_response(self, request, context): | ||
1378 | 104 | response = HttpResponse('You can\'t delete this vote.') | ||
1379 | 105 | response.status_code = 403 | ||
1380 | 106 | return response | ||
1381 | 107 | |||
1382 | 108 | def invalid_field_response(self, request, context): | ||
1383 | 109 | response = HttpResponse('Invalid field name.') | ||
1384 | 110 | response.status_code = 403 | ||
1385 | 111 | return response | ||
1386 | 112 | |||
1387 | 113 | def invalid_rating_response(self, request, context): | ||
1388 | 114 | response = HttpResponse('Invalid rating value.') | ||
1389 | 115 | response.status_code = 403 | ||
1390 | 116 | return response | ||
1391 | 117 | |||
1392 | 118 | def get_instance(self, content_type_id, object_id): | ||
1393 | 119 | return ContentType.objects.get(pk=content_type_id)\ | ||
1394 | 120 | .get_object_for_this_type(pk=object_id) | ||
1395 | 121 | |||
1396 | 122 | |||
1397 | 123 | class AddRatingFromModel(AddRatingView): | ||
1398 | 124 | |||
1399 | 125 | def __call__(self, request, model, app_label, object_id, field_name, score): | ||
1400 | 126 | """__call__(request, model, app_label, object_id, field_name, score) | ||
1401 | 127 | |||
1402 | 128 | Adds a vote to the specified model field. | ||
1403 | 129 | |||
1404 | 130 | """ | ||
1405 | 131 | try: | ||
1406 | 132 | content_type = ContentType.objects.get( | ||
1407 | 133 | model=model, app_label=app_label) | ||
1408 | 134 | except ContentType.DoesNotExist: | ||
1409 | 135 | raise Http404('Invalid `model` or `app_label`.') | ||
1410 | 136 | |||
1411 | 137 | return super(AddRatingFromModel, self).__call__(request, content_type.id, | ||
1412 | 138 | object_id, field_name, score) | ||
1413 | 139 | 0 | ||
1414 | === modified file 'media/css/base.css' | |||
1415 | --- media/css/base.css 2018-11-18 16:16:04 +0000 | |||
1416 | +++ media/css/base.css 2018-11-21 06:22:00 +0000 | |||
1417 | @@ -108,7 +108,7 @@ | |||
1418 | 108 | font-weight: bold; | 108 | font-weight: bold; |
1419 | 109 | } | 109 | } |
1420 | 110 | 110 | ||
1422 | 111 | .star-ratings input, button { | 111 | .star-ratings input, .star-ratings button { |
1423 | 112 | padding: inherit; | 112 | padding: inherit; |
1424 | 113 | } | 113 | } |
1425 | 114 | 114 | ||
1426 | 115 | 115 | ||
1427 | === modified file 'settings.py' | |||
1428 | --- settings.py 2018-11-19 17:19:29 +0000 | |||
1429 | +++ settings.py 2018-11-21 06:22:00 +0000 | |||
1430 | @@ -111,7 +111,6 @@ | |||
1431 | 111 | 'dj_pagination', | 111 | 'dj_pagination', |
1432 | 112 | 'tagging', | 112 | 'tagging', |
1433 | 113 | 'star_ratings', | 113 | 'star_ratings', |
1434 | 114 | 'djangoratings', # included as wlapp | ||
1435 | 115 | ] | 114 | ] |
1436 | 116 | 115 | ||
1437 | 117 | MIDDLEWARE = [ | 116 | MIDDLEWARE = [ |
1438 | 118 | 117 | ||
1439 | === modified file 'templates/wlmaps/base.html' | |||
1440 | --- templates/wlmaps/base.html 2018-11-18 11:31:20 +0000 | |||
1441 | +++ templates/wlmaps/base.html 2018-11-21 06:22:00 +0000 | |||
1442 | @@ -1,6 +1,5 @@ | |||
1443 | 1 | {% extends "base.html" %} | 1 | {% extends "base.html" %} |
1444 | 2 | {% load static %} | 2 | {% load static %} |
1445 | 3 | |||
1446 | 4 | {% comment %} | 3 | {% comment %} |
1447 | 5 | vim:ft=htmldjango | 4 | vim:ft=htmldjango |
1448 | 6 | {% endcomment %} | 5 | {% endcomment %} |
1449 | @@ -8,7 +7,7 @@ | |||
1450 | 8 | {% block extra_head %} | 7 | {% block extra_head %} |
1451 | 9 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/forum.css" /> | 8 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/forum.css" /> |
1452 | 10 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/maps.css" /> | 9 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/maps.css" /> |
1454 | 11 | <link rel="stylesheet" href="{% static 'star-ratings/css/star-ratings.css' %}"> | 10 | <link rel="stylesheet" href="{% static 'star-ratings/css/star-ratings.css' %}" /> |
1455 | 12 | <script type="text/javascript" src="{% static 'star-ratings/js/dist/star-ratings.min.js' %}"></script> | 11 | <script type="text/javascript" src="{% static 'star-ratings/js/dist/star-ratings.min.js' %}"></script> |
1456 | 13 | {{block.super}} | 12 | {{block.super}} |
1457 | 14 | {% endblock %} | 13 | {% endblock %} |
1458 | 15 | 14 | ||
1459 | === modified file 'templates/wlmaps/index.html' | |||
1460 | --- templates/wlmaps/index.html 2018-11-18 16:16:04 +0000 | |||
1461 | +++ templates/wlmaps/index.html 2018-11-21 06:22:00 +0000 | |||
1462 | @@ -5,11 +5,9 @@ | |||
1463 | 5 | 5 | ||
1464 | 6 | {% load custom_date %} | 6 | {% load custom_date %} |
1465 | 7 | {% load wlprofile_extras %} | 7 | {% load wlprofile_extras %} |
1466 | 8 | {% load wlmaps_extra %} | ||
1467 | 9 | {% load threadedcommentstags %} | 8 | {% load threadedcommentstags %} |
1468 | 10 | {% load pagination_tags %} | 9 | {% load pagination_tags %} |
1469 | 11 | {% load ratings %} | 10 | {% load ratings %} |
1470 | 12 | {% load ratings_old %} | ||
1471 | 13 | 11 | ||
1472 | 14 | {% block content_header %} | 12 | {% block content_header %} |
1473 | 15 | <h1>Maps</h1> | 13 | <h1>Maps</h1> |
1474 | @@ -59,7 +57,7 @@ | |||
1475 | 59 | <td class="grey">Rating:</td> | 57 | <td class="grey">Rating:</td> |
1476 | 60 | <td> | 58 | <td> |
1477 | 61 | {% ratings map read_only template_name='star_rating/average.html' %} | 59 | {% ratings map read_only template_name='star_rating/average.html' %} |
1479 | 62 | {{ map.rating|average_rating }} ({{ map.rating.votes }} Votes)</td> | 60 | </td> |
1480 | 63 | <td class="spacer"></td> | 61 | <td class="spacer"></td> |
1481 | 64 | {% get_comment_count for map as ccount %} | 62 | {% get_comment_count for map as ccount %} |
1482 | 65 | <td class="grey">Comments:</td><td>{{ ccount }}</td> | 63 | <td class="grey">Comments:</td><td>{{ ccount }}</td> |
1483 | 66 | 64 | ||
1484 | === modified file 'templates/wlmaps/map_detail.html' | |||
1485 | --- templates/wlmaps/map_detail.html 2018-11-18 16:16:04 +0000 | |||
1486 | +++ templates/wlmaps/map_detail.html 2018-11-21 06:22:00 +0000 | |||
1487 | @@ -4,35 +4,16 @@ | |||
1488 | 4 | {% endcomment %} | 4 | {% endcomment %} |
1489 | 5 | 5 | ||
1490 | 6 | {% load custom_date %} | 6 | {% load custom_date %} |
1491 | 7 | {% load wlmaps_extra %} | ||
1492 | 8 | {% load wlprofile_extras %} | 7 | {% load wlprofile_extras %} |
1493 | 9 | {% load threadedcommentstags %} | 8 | {% load threadedcommentstags %} |
1494 | 10 | {% load wl_markdown %} | 9 | {% load wl_markdown %} |
1495 | 11 | {% load ratings %} | 10 | {% load ratings %} |
1496 | 12 | {% load ratings_old %} | ||
1497 | 13 | 11 | ||
1498 | 14 | {% block title %}{{ map.name }} - {{ block.super }}{% endblock %} | 12 | {% block title %}{{ map.name }} - {{ block.super }}{% endblock %} |
1499 | 15 | 13 | ||
1500 | 16 | {% block extra_head %} | 14 | {% block extra_head %} |
1501 | 17 | {{ block.super }} | 15 | {{ block.super }} |
1520 | 18 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/comments.css" /> | 16 | <link rel="stylesheet" type="text/css" media="all" href="{{ MEDIA_URL }}css/comments.css" /> |
1503 | 19 | {% if not user.is_anonymous %} | ||
1504 | 20 | <script src="{{ MEDIA_URL}}/js/jquery.sexy-vote.js" type="text/javascript"></script> | ||
1505 | 21 | <script type="text/javascript"> | ||
1506 | 22 | $(function() { | ||
1507 | 23 | $('#vote').sexyVote( { | ||
1508 | 24 | activeImageSrc: "{{ MEDIA_URL }}img/active_star.gif", | ||
1509 | 25 | passiveImageSrc: "{{ MEDIA_URL }}img/passive_star.gif", | ||
1510 | 26 | maxScore: 10, | ||
1511 | 27 | messages: ["","","","","","","","","",""], | ||
1512 | 28 | fn: function(e, score) { | ||
1513 | 29 | $.post("{% url 'wlmaps_rate' map.slug %}",{ vote: score }); | ||
1514 | 30 | } | ||
1515 | 31 | }); | ||
1516 | 32 | }); | ||
1517 | 33 | </script> | ||
1518 | 34 | |||
1519 | 35 | {% endif %} | ||
1521 | 36 | {% endblock %} | 17 | {% endblock %} |
1522 | 37 | 18 | ||
1523 | 38 | {% block content_header %} | 19 | {% block content_header %} |
1524 | @@ -97,13 +78,8 @@ | |||
1525 | 97 | </tr> | 78 | </tr> |
1526 | 98 | <tr> | 79 | <tr> |
1527 | 99 | <td class="grey">Rating:</td> | 80 | <td class="grey">Rating:</td> |
1535 | 100 | <td>{% ratings map template_name='star_rating/rate.html' %} | 81 | <td> |
1536 | 101 | {{ map.rating|average_rating }} ({{ map.rating.votes }} Votes) | 82 | {% ratings map template_name='star_rating/rate.html' %} |
1530 | 102 | {% if not user.is_anonymous %} | ||
1531 | 103 | <span id="vote"></span> | ||
1532 | 104 | {% else %} | ||
1533 | 105 | - Login to vote | ||
1534 | 106 | {% endif %} | ||
1537 | 107 | </td> | 83 | </td> |
1538 | 108 | </tr> | 84 | </tr> |
1539 | 109 | <tr> | 85 | <tr> |
1540 | 110 | 86 | ||
1541 | === added file 'wlmaps/migrations/0002_auto_20181119_1855.py' | |||
1542 | --- wlmaps/migrations/0002_auto_20181119_1855.py 1970-01-01 00:00:00 +0000 | |||
1543 | +++ wlmaps/migrations/0002_auto_20181119_1855.py 2018-11-21 06:22:00 +0000 | |||
1544 | @@ -0,0 +1,33 @@ | |||
1545 | 1 | # -*- coding: utf-8 -*- | ||
1546 | 2 | # Generated by Django 1.11.12 on 2018-11-19 18:55 | ||
1547 | 3 | from __future__ import unicode_literals | ||
1548 | 4 | |||
1549 | 5 | from django.db import migrations, models | ||
1550 | 6 | |||
1551 | 7 | |||
1552 | 8 | class Migration(migrations.Migration): | ||
1553 | 9 | |||
1554 | 10 | dependencies = [ | ||
1555 | 11 | ('wlmaps', '0001_initial'), | ||
1556 | 12 | ] | ||
1557 | 13 | |||
1558 | 14 | operations = [ | ||
1559 | 15 | migrations.RemoveField( | ||
1560 | 16 | model_name='map', | ||
1561 | 17 | name='rating_score', | ||
1562 | 18 | ), | ||
1563 | 19 | migrations.RemoveField( | ||
1564 | 20 | model_name='map', | ||
1565 | 21 | name='rating_votes', | ||
1566 | 22 | ), | ||
1567 | 23 | migrations.AlterField( | ||
1568 | 24 | model_name='map', | ||
1569 | 25 | name='hint', | ||
1570 | 26 | field=models.TextField(blank=True, verbose_name=b'Hint'), | ||
1571 | 27 | ), | ||
1572 | 28 | migrations.AlterField( | ||
1573 | 29 | model_name='map', | ||
1574 | 30 | name='world_name', | ||
1575 | 31 | field=models.CharField(blank=True, max_length=50), | ||
1576 | 32 | ), | ||
1577 | 33 | ] | ||
1578 | 0 | 34 | ||
1579 | === modified file 'wlmaps/models.py' | |||
1580 | --- wlmaps/models.py 2018-11-18 16:16:04 +0000 | |||
1581 | +++ wlmaps/models.py 2018-11-21 06:22:00 +0000 | |||
1582 | @@ -11,8 +11,6 @@ | |||
1583 | 11 | except ImportError: | 11 | except ImportError: |
1584 | 12 | notification = None | 12 | notification = None |
1585 | 13 | 13 | ||
1586 | 14 | from djangoratings.fields import AnonymousRatingField | ||
1587 | 15 | |||
1588 | 16 | 14 | ||
1589 | 17 | class Map(models.Model): | 15 | class Map(models.Model): |
1590 | 18 | name = models.CharField(max_length=255, unique=True) | 16 | name = models.CharField(max_length=255, unique=True) |
1591 | @@ -38,7 +36,6 @@ | |||
1592 | 38 | nr_downloads = models.PositiveIntegerField( | 36 | nr_downloads = models.PositiveIntegerField( |
1593 | 39 | verbose_name='Download count', default=0) | 37 | verbose_name='Download count', default=0) |
1594 | 40 | 38 | ||
1595 | 41 | rating = AnonymousRatingField(range=10, can_change_vote=True) | ||
1596 | 42 | 39 | ||
1597 | 43 | class Meta: | 40 | class Meta: |
1598 | 44 | ordering = ('-pub_date',) | 41 | ordering = ('-pub_date',) |
1599 | 45 | 42 | ||
1600 | === removed directory 'wlmaps/templatetags' | |||
1601 | === removed file 'wlmaps/templatetags/__init__.py' | |||
1602 | === removed file 'wlmaps/templatetags/wlmaps_extra.py' | |||
1603 | --- wlmaps/templatetags/wlmaps_extra.py 2018-11-18 16:16:04 +0000 | |||
1604 | +++ wlmaps/templatetags/wlmaps_extra.py 1970-01-01 00:00:00 +0000 | |||
1605 | @@ -1,15 +0,0 @@ | |||
1606 | 1 | #!/usr/bin/env python -tt | ||
1607 | 2 | # encoding: utf-8 | ||
1608 | 3 | |||
1609 | 4 | from django import template | ||
1610 | 5 | |||
1611 | 6 | register = template.Library() | ||
1612 | 7 | |||
1613 | 8 | |||
1614 | 9 | @register.filter | ||
1615 | 10 | def average_rating(rating): | ||
1616 | 11 | if rating.votes > 0: | ||
1617 | 12 | avg = '%.1f' % (float(rating.score) / rating.votes) | ||
1618 | 13 | else: | ||
1619 | 14 | avg = '0.0' | ||
1620 | 15 | return 'Old: {}'.format(avg) | ||
1621 | 16 | 0 | ||
1622 | === modified file 'wlmaps/tests/test_views.py' | |||
1623 | --- wlmaps/tests/test_views.py 2018-04-08 14:40:17 +0000 | |||
1624 | +++ wlmaps/tests/test_views.py 2018-11-21 06:22:00 +0000 | |||
1625 | @@ -143,66 +143,3 @@ | |||
1626 | 143 | reverse('wlmaps_view', args=('a-map-that-doesnt-exist',))) | 143 | reverse('wlmaps_view', args=('a-map-that-doesnt-exist',))) |
1627 | 144 | self.assertEqual(c.status_code, 404) | 144 | self.assertEqual(c.status_code, 404) |
1628 | 145 | 145 | ||
1629 | 146 | |||
1630 | 147 | ############ | ||
1631 | 148 | # RATING # | ||
1632 | 149 | ############ | ||
1633 | 150 | class TestWLMapsViews_Rating(_LoginToSite): | ||
1634 | 151 | |||
1635 | 152 | def setUp(self): | ||
1636 | 153 | _LoginToSite.setUp(self) | ||
1637 | 154 | |||
1638 | 155 | # Add maps | ||
1639 | 156 | nm = Map.objects.create( | ||
1640 | 157 | name='Map', | ||
1641 | 158 | author='Author', | ||
1642 | 159 | w=128, | ||
1643 | 160 | h=64, | ||
1644 | 161 | nr_players=4, | ||
1645 | 162 | descr='a good map to play with', | ||
1646 | 163 | minimap='/wlmaps/minimaps/Map.png', | ||
1647 | 164 | world_name='blackland', | ||
1648 | 165 | |||
1649 | 166 | uploader=self.user, | ||
1650 | 167 | uploader_comment='Rockdamap' | ||
1651 | 168 | ) | ||
1652 | 169 | nm.save() | ||
1653 | 170 | self.map = nm | ||
1654 | 171 | |||
1655 | 172 | def test_RatingNonExistingMap_Except404(self): | ||
1656 | 173 | c = self.client.post( | ||
1657 | 174 | reverse('wlmaps_rate', args=('a-map-that-doesnt-exist',)), | ||
1658 | 175 | {'vote': 10}) | ||
1659 | 176 | self.assertEqual(c.status_code, 404) | ||
1660 | 177 | |||
1661 | 178 | def test_RatingGet_Except405(self): | ||
1662 | 179 | c = self.client.get( | ||
1663 | 180 | reverse('wlmaps_rate', args=('map',)), | ||
1664 | 181 | {'vote': 10}) | ||
1665 | 182 | self.assertEqual(c.status_code, 405) | ||
1666 | 183 | |||
1667 | 184 | def test_RatingInvalidValue_Except400(self): | ||
1668 | 185 | c = self.client.post( | ||
1669 | 186 | reverse('wlmaps_rate', args=('map',)), | ||
1670 | 187 | {'vote': 11}) | ||
1671 | 188 | self.assertEqual(c.status_code, 400) | ||
1672 | 189 | |||
1673 | 190 | def test_RatingNonIntegerValue_Except400(self): | ||
1674 | 191 | c = self.client.post( | ||
1675 | 192 | reverse('wlmaps_rate', args=('map',)), | ||
1676 | 193 | {'vote': 'shubidu'}) | ||
1677 | 194 | self.assertEqual(c.status_code, 400) | ||
1678 | 195 | |||
1679 | 196 | def test_RatingExistingMap_ExceptCorrectResult(self): | ||
1680 | 197 | c = self.client.post( | ||
1681 | 198 | reverse('wlmaps_rate', args=('map',)), | ||
1682 | 199 | {'vote': 7}) | ||
1683 | 200 | # Except redirect | ||
1684 | 201 | self.assertEqual(c.status_code, 302) | ||
1685 | 202 | |||
1686 | 203 | # We have to refetch this map, because | ||
1687 | 204 | # votes doesn't hit the database | ||
1688 | 205 | m = Map.objects.get(slug='map') | ||
1689 | 206 | |||
1690 | 207 | self.assertEqual(m.rating.votes, 1) | ||
1691 | 208 | self.assertEqual(m.rating.score, 7) | ||
1692 | 209 | 146 | ||
1693 | === modified file 'wlmaps/urls.py' | |||
1694 | --- wlmaps/urls.py 2016-12-13 18:28:51 +0000 | |||
1695 | +++ wlmaps/urls.py 2018-11-21 06:22:00 +0000 | |||
1696 | @@ -4,6 +4,7 @@ | |||
1697 | 4 | from models import Map | 4 | from models import Map |
1698 | 5 | from views import * | 5 | from views import * |
1699 | 6 | 6 | ||
1700 | 7 | |||
1701 | 7 | urlpatterns = [ | 8 | urlpatterns = [ |
1702 | 8 | url(r'^$', index, name='wlmaps_index'), | 9 | url(r'^$', index, name='wlmaps_index'), |
1703 | 9 | url(r'^upload/$', upload, name='wlmaps_upload'), | 10 | url(r'^upload/$', upload, name='wlmaps_upload'), |
1704 | @@ -14,7 +15,4 @@ | |||
1705 | 14 | edit_comment, name='wlmaps_edit_comment'), | 15 | edit_comment, name='wlmaps_edit_comment'), |
1706 | 15 | url(r'^(?P<map_slug>[-\w]+)/download/$', | 16 | url(r'^(?P<map_slug>[-\w]+)/download/$', |
1707 | 16 | download, name='wlmaps_download'), | 17 | download, name='wlmaps_download'), |
1708 | 17 | |||
1709 | 18 | url(r'^(?P<map_slug>[-\w]+)/rate/$', | ||
1710 | 19 | rate, name='wlmaps_rate'), | ||
1711 | 20 | ] | 18 | ] |
1712 | 21 | 19 | ||
1713 | === modified file 'wlmaps/views.py' | |||
1714 | --- wlmaps/views.py 2018-04-08 14:40:17 +0000 | |||
1715 | +++ wlmaps/views.py 2018-11-21 06:22:00 +0000 | |||
1716 | @@ -24,31 +24,6 @@ | |||
1717 | 24 | }) | 24 | }) |
1718 | 25 | 25 | ||
1719 | 26 | 26 | ||
1720 | 27 | def rate(request, map_slug): | ||
1721 | 28 | """Rate a given map.""" | ||
1722 | 29 | if request.method != 'POST': | ||
1723 | 30 | return HttpResponseNotAllowed(['post']) | ||
1724 | 31 | |||
1725 | 32 | m = get_object_or_404(models.Map, slug=map_slug) | ||
1726 | 33 | |||
1727 | 34 | if not 'vote' in request.POST: | ||
1728 | 35 | return HttpResponseBadRequest() | ||
1729 | 36 | try: | ||
1730 | 37 | val = int(request.POST['vote']) | ||
1731 | 38 | except ValueError: | ||
1732 | 39 | return HttpResponseBadRequest() | ||
1733 | 40 | |||
1734 | 41 | if not (0 < val <= 10): | ||
1735 | 42 | return HttpResponseBadRequest() | ||
1736 | 43 | |||
1737 | 44 | m.rating.add(score=val, user=request.user, | ||
1738 | 45 | ip_address=get_real_ip(request)) | ||
1739 | 46 | |||
1740 | 47 | # m.save() is called in djangoratings.fields.add for each instance | ||
1741 | 48 | |||
1742 | 49 | return HttpResponseRedirect(reverse('wlmaps_view', kwargs= {'map_slug': m.slug})) | ||
1743 | 50 | |||
1744 | 51 | |||
1745 | 52 | def download(request, map_slug): | 27 | def download(request, map_slug): |
1746 | 53 | """Very simple view that just returns the binary data of this map and | 28 | """Very simple view that just returns the binary data of this map and |
1747 | 54 | increases the download count.""" | 29 | increases the download count.""" |
LGTM :)