1 [[!meta title="Beleščák Jakub"]]
3 **Název projektu:** Sphene Community Tools
5 **Domovská stránka projektu:** <http://sct.sphene.net/wiki/show/Start/>
10 Do existujiciho projektu Sphene Community Tools, coz je komunitni portal s wiki, forem a blogem, postaveny na frameworku Django, pridam pocitani karmy. Tj, moznost hodnotit jednotlive prispevky ve foru a uzivatelum pocitat jejich "kvalitu".
12 Kazdy prispevek ve foru dostane 2 tlacitka pro libi/nelibi. Bude se pocitat hodnoceni jednotlivych prispevku a rovnez hodnoceni uzivatelu. Hodnoceni uzivatelu bude zhodnocovat:
14 - Dobu po kterou jsou zaregistrovani/neaktivni
16 - Hodnoceni vlastnich prispevku
18 Hodnoceni nesmi prilis favorizovat nektere skupiny uzivatelu (jiz dlouho registrovane...)
23 Výsledné řešení splňuje požadavky, tj: máme karmu, hodnoceni libi/nelibi, a hodnocení zahrnující výše zmíněná kriteria. Karma se dává v rozsahu (-4,4) a k tomu jsou mapovány úrovně červ, krysa, kráva, člověk, moudrý člověk a bůh.
25 Rovnice pro výpočet je následující
29 (arctan vypadá asi takhle :-),
33 čímž je docíleno, že k nejhorším/nejlepším karmám se člověk blíží mnohem pomaleji, než kolem karmy neutrální.
35 Výsledek pak vypadá asi takto
42 Komunikace s hlavním (a téměř jediným) vývojářem SCT byla velice vstřícná a bylo znát jeho nadšení o můj zájem. Avšak vzhledem k tomu, že nemá žádné jasně dané a formalizované procesy, tak nemůžu tušit, kdy se můj diff objeví ve vývojové větvi a zda-li krom prostého "díky" se jím bude vlastně zabývat.
48 * [[Prezentace záměru práce|sct.odp]]
49 * [[Prezentace výdledku práce|sct_vysledek.odp]]
54 <a href='https://www.ohloh.net/accounts/76324?ref=Detailed'>
55 <img alt='Ohloh profile for Jakub Beleščák' height='35' src='https://www.ohloh.net/accounts/76324/widgets/account_detailed.gif' width='191' />
67 Herbert Poul <herbert@poul.at>
69 Fri, 12 Mar 2010 10:03:48 +0200
71 Jakub Beles(c(ák <jakub.belescak@centrum.cz>
75 the diff looks ok, i will merge it when i have some more time
80 > > On Thu, Mar 11, 2010 at 12:44 PM, Jakub Beles(c(ák
81 > > <jakub.belescak@centrum.cz> wrote:
83 > > well the formula is still being tested and is discussed a lot, but it
85 > > depends on the concrete community , its size etc...
87 > > I was thinking of making it optional, but that would require for the
89 > > values to be accessible in templates, through which is the karma and
90 > > its controls displayed.
91 > > It would be really gr8, to take karma to account to the heat calculation,
92 > > but I can not decypher, how this "call function by its name stored in
96 > > Have a nice weekend
100 > > Herbert Poul wrote:
103 >> >> i am very glad to hear that you want to contribute part of your work
104 >> >> back. If you like we can further discuss the features (although i
105 >> >> guess since you know more about your requirements and have thought
106 >> >> more deeply about it i can't necessary comment much about your
107 >> >> suggested formular :) but if you need help for how to integrate it
108 >> >> into SCT code i might be able to give a hint.. )
109 >> >> for me the best way would be if you could send me a diff (ie. patch)
110 >> >> to 0.6 - if you like i could even give you write permission to the SVN
111 >> >> repository so you can develop this in a SCT branch (if you don't use
112 >> >> your own SVN already)..
114 >> >> to your idea: this sounds very good - for the periodic calculation:
115 >> >> there is already a maintenance signal
116 >> >> (sphene.community.signals.maintenance) which is used for the 'heat'
117 >> >> calculation of forum threads - maybe it would make sense to use the
118 >> >> same? (it is triggered with the 'sph_maintenance' management command)
120 >> >> i'm looking forward to your changes ;) do you think it is possible to
121 >> >> make this feature optional (ie. so it can be turned off using a
122 >> >> SPH_SETTINGS option)?
128 >> >> On Mon, Feb 22, 2010 at 12:04 PM, Jakub Beles(c(ák
129 >> >> <jakub.belescak@centrum.cz> wrote:
131 >>> >>> We've decided to use SCT as community portal for one small faculty.
133 >>> >>> they wanted, that wasn't in SCT is karma. I would like to
134 >>> >>> participate in
135 >>> >>> SCT development. So, what I can do now is:
136 >>> >>> 1) Discuss what is the best way to implement it.
137 >>> >>> 2) Send you changed version 0.6
138 >>> >>> 3) Send you diff from 0.6
139 >>> >>> 4) Forget about it, because it is completely useless
141 >>> >>> Which one would you like? :-)
144 >>> >>> My idea, and what I've partly implemented is:
145 >>> >>> 1) posts have +,-. 1 user - 1 post - 1 vote
146 >>> >>> 2) users have karma, which is
147 >>> >>> a) precounted in database
148 >>> >>> b) calculated with a function, which can be user overiden
149 >>> >>> c) displayed with a template tag in interval -4,4
150 >>> >>> d) precounted periodically by management/command
152 >>> >>> so far, my colleauge suggested this calculation function
153 >>> >>> karma = 4 * 2/pi * arctan k*[ 0.03*(today-registered) + 0.01*num_posts
154 >>> >>> - 0.03*(today-last_post) + sum{karma_post*(2*karma_author + 1)} ]
155 >>> >>> the coefficients (4, 0.03, 0.01 and 0.03) will be put in SPH_SETTINGS
157 >>> >>> Jakub Belescak
159 >>> >>> PS: We are also considering integrating user2user messaging, perhaps
160 >>> >>> instant user2user messaging. http://code.google.com/p/django-messages/
161 >>> >>> http://code.google.com/p/django-messaging/
170 Diff oproti verzi 0.6
172 Index: communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py
173 ===================================================================
174 --- communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy)
175 +++ communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3)
177 -from sphene.community.models import CommunityUserProfile
178 -from django import template
179 -register = template.Library()
181 -from sphene.sphboard.models import Post, PostKarmers
182 -from django.contrib.auth.models import User
183 -from django.conf import settings
187 -def calculate_karma_logic(community_user_profile):
188 - karma_linearity_koeficient = 0.1 #if not hasattr(settings, 'SPH_SETTINGS') or not 'karma_linearity_koeficient' in settings.SPH_SETTINGS else settings.SPH_SETTINGS['karma_linearity_koeficient']
189 - karma_age_koeficient = 0.02 #if not hasattr(settings, 'SPH_SETTINGS') or not 'karma_linearity_koeficient' in settings.SPH_SETTINGS else settings.SPH_SETTINGS['karma_age_koeficient']
190 - karma_post_count_koeficient = 0.05 #if not hasattr(settings, 'SPH_SETTINGS') or not 'karma_linearity_koeficient' in settings.SPH_SETTINGS else settings.SPH_SETTINGS['karma_post_count_koeficient']
191 - karma_inactivity_koeficient = 0.005 #if not hasattr(settings, 'SPH_SETTINGS') or not 'karma_linearity_koeficient' in settings.SPH_SETTINGS else settings.SPH_SETTINGS['karma_inactivity_koeficient']
192 - us = community_user_profile
194 - age = (datetime.datetime.today() - us.user.date_joined).days
195 - posts = Post.objects.filter(author=us.user,is_hidden=0).order_by('-postdate')
196 - post_count = len(posts)
198 - last_post = posts[0].postdate
200 - last_post = us.user.date_joined
201 - inactivity = (datetime.datetime.today()-last_post).days
203 - for post1 in posts:
204 - for pok in PostKarmers.objects.filter(post = post1):
206 - sumicka+= 2*pok.user.communityuserprofile_set.all()[0].karma+1
208 - sumicka-= 2*pok.user.communityuserprofile_set.all()[0].karma+1
209 - return 2/math.pi * math.atan( karma_linearity_koeficient * ( karma_age_koeficient*age + karma_post_count_koeficient*post_count - karma_inactivity_koeficient*inactivity + sumicka ) )
211 -def get_karma(community_user_profile):
212 - if community_user_profile.karma_counted_date != community_user_profile.karma_counted_date.today( ):
213 - community_user_profile.karma_counted_date = community_user_profile.karma_counted_date.today( )
214 - community_user_profile.karma = calculate_karma_logic(community_user_profile)
215 - community_user_profile.save()
216 - return community_user_profile.karma
218 -@register.simple_tag
219 -def sph_user_karma(userv):
222 - usr = CommunityUserProfile.objects.get( user__id = userv.id )
223 -# usr = userv.communityuserprofile_set.all()[0]
224 - except CommunityUserProfile.DoesNotExist:
225 - usr = CommunityUserProfile(user = userv)
228 - karma = round(4*get_karma(usr),2)
230 - karma = round(4*get_karma(usr),2)
234 - return "+"+str(karma)
238 Index: communitytools/sphenecoll/sphene/community/models.py
239 ===================================================================
240 --- communitytools/sphenecoll/sphene/community/models.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy)
241 +++ communitytools/sphenecoll/sphene/community/models.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3)
243 avatar_height = models.IntegerField(blank = True, null = True, )
244 avatar_width = models.IntegerField(blank = True, null = True, )
246 - karma = models.FloatField(default = 0, null = False, editable = False)
247 - karma_counted_date = models.DateField(auto_now_add=True, editable = False)
248 - def karma_counted(self):
249 -# if self.karma_counted_date != self.karma_counted_date.today( ):
250 -# self.karma = calculate_karma_logic(self)
254 changelog = ( ( '2007-08-10 00', 'alter', 'ADD avatar varchar(100)' ),
255 ( '2007-08-10 01', 'alter', 'ADD avatar_height integer' ),
257 ( '2008-04-10 00', 'alter', 'ADD displayname varchar(250)' ),
258 ( '2008-04-10 01', 'update', "SET displayname = ''" ),
259 ( '2008-04-10 02', 'alter', 'ALTER displayname SET NOT NULL' ),
260 - ( '2009-01-16 00', 'alter', 'ADD karma integer NOT NULL', ),
261 - ( '2009-01-16 01', 'alter', 'ALTER COLUMN karma SET DEFAULT 0', ),
262 - ( '2009-02-22 00', 'alter', 'ALTER COLUMN karma TYPE float', ),
263 - ( '2009-02-22 01', 'update', 'SET karma = 0', ),
264 - ( '2009-02-24 00', 'alter', 'ADD karma_counted_date date NOT NULL DEFAULT NOW()', ),
269 Index: communitytools/sphenecoll/sphene/sphboard/views.py
270 ===================================================================
271 --- communitytools/sphenecoll/sphene/sphboard/views.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy)
272 +++ communitytools/sphenecoll/sphene/sphboard/views.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3)
274 from sphene.community import PermissionDenied
275 from sphene.community.middleware import get_current_sphdata
276 from sphene.community.sphutils import sph_reverse, get_user_displayname, format_date, get_sph_setting, add_rss_feed, sph_render_to_response
277 -from sphene.community.models import CommunityUserProfile
279 from sphene.generic import advanced_object_list as objlist
281 from sphene.sphboard.forms import PollForm, PollChoiceForm, PostForm, \
282 PostPollForm, PostAttachmentForm, \
283 AnnotateForm, MoveAndAnnotateForm, MovePostForm
284 -from sphene.sphboard.models import Category, Post, PostAnnotation, ThreadInformation, Poll, PollChoice, PollVoters, POST_MARKUP_CHOICES, THREAD_TYPE_MOVED, THREAD_TYPE_DEFAULT, get_all_viewable_categories, ThreadLastVisit, CategoryLastVisit, PostKarmers
285 +from sphene.sphboard.models import Category, Post, PostAnnotation, ThreadInformation, Poll, PollChoice, PollVoters, POST_MARKUP_CHOICES, THREAD_TYPE_MOVED, THREAD_TYPE_DEFAULT, get_all_viewable_categories, ThreadLastVisit, CategoryLastVisit
288 def showCategory(request, group, category_id = None, showType = None, slug = None):
293 -def karmize(request, post_id, like):
294 - like = False if like=='0' else True
295 - postobj = Post.objects.get(id=post_id)
296 - if postobj._allow_karma():
297 - postobj.karma_touch(like)
298 - pk = PostKarmers(post = postobj, user = request.user, like = like)
300 - return HttpResponseRedirect( request.GET['next'] )
302 \ No newline at end of file
304 Index: communitytools/sphenecoll/sphene/sphboard/models.py
305 ===================================================================
306 --- communitytools/sphenecoll/sphene/sphboard/models.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy)
307 +++ communitytools/sphenecoll/sphene/sphboard/models.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3)
309 from django.db import models
310 from django.db.models import Q
311 from django.db.models import signals
312 -from django.db.models import Count
313 from django.core.urlresolvers import reverse
314 from django.core.mail import send_mass_mail
315 from django.core.cache import cache
317 # a custom category type might change this behavior tough by adding a
318 # administration interface for hidden posts.)
319 is_hidden = models.IntegerField(default = 0, editable = False, db_index = True )
321 - karma = models.IntegerField(default = 0, null = False, editable = False)
322 - #like increases karma, not like decreases
323 - def karma_touch(self, like):
325 - self.karma = self.karma+1
327 - self.karma = self.karma-1
329 - #whether the active user has already voted karma
330 - def _allow_karma(self, user = None):
331 - if user == None: user = get_current_user()
332 - if not user or not user.is_authenticated() or user==self.author:
336 - pk = PostKarmers.objects.get(post = self, user = user)
338 - except PostKarmers.DoesNotExist:
340 - allow_karma = property(fget=_allow_karma)
341 - #how has the current user voted
342 - def _my_karma(self, user = None):
343 - if user == None: user = get_current_user()
345 - pk = PostKarmers.objects.get(post = self, user = user)
347 - except PostKarmers.DoesNotExist:
349 - my_karma = property(fget=_my_karma)
350 - def _positive_karma(self):
351 - return PostKarmers.objects.filter(post = self, like = True).aggregate(Count('id'))['id__count']
352 - positive_karma = property(fget=_positive_karma)
353 - def _negative_karma(self):
354 - return PostKarmers.objects.filter(post = self, like = False).aggregate(Count('id'))['id__count']
355 - negative_karma = property(fget=_negative_karma)
357 # allobjects also contain hidden posts.
358 allobjects = models.Manager()
359 # objects only contains non-hidden posts.
361 ( '2008-01-06 00', 'alter', 'ADD is_hidden INTEGER', ),
362 ( '2008-01-06 01', 'update', 'SET is_hidden = 0', ),
363 ( '2008-01-06 02', 'alter', 'ALTER is_hidden SET NOT NULL', ),
364 - ( '2009-01-16 00', 'alter', 'ADD karma integer NOT NULL', ),
365 - ( '2009-01-16 01', 'alter', 'ALTER COLUMN karma SET DEFAULT 0', ),
370 if not user or not user.is_authenticated():
374 + if user.is_superuser \
375 or has_permission_flag( user, 'sphboard_hideallposts', self.category ):
378 @@ -1470,12 +1432,6 @@
379 verbose_name = ugettext_lazy('Poll voter')
380 verbose_name_plural = ugettext_lazy('Poll voters')
382 -class PostKarmers(models.Model):
383 - post = models.ForeignKey( Post, editable = False)
384 - user = models.ForeignKey( User, editable = False)
385 - like = models.BooleanField( editable = False)
387 - unique_together = (( 'post', 'user' ),)
389 class BoardUserProfile(models.Model):
390 user = models.ForeignKey( User, unique = True)
392 Index: communitytools/sphenecoll/sphene/sphboard/views.py
393 ===================================================================
394 --- communitytools/sphenecoll/sphene/sphboard/views.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy)
395 +++ communitytools/sphenecoll/sphene/sphboard/views.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3)
397 from sphene.community import PermissionDenied
398 from sphene.community.middleware import get_current_sphdata
399 from sphene.community.sphutils import sph_reverse, get_user_displayname, format_date, get_sph_setting, add_rss_feed, sph_render_to_response
400 -from sphene.community.models import CommunityUserProfile
402 from sphene.generic import advanced_object_list as objlist
404 from sphene.sphboard.forms import PollForm, PollChoiceForm, PostForm, \
405 PostPollForm, PostAttachmentForm, \
406 AnnotateForm, MoveAndAnnotateForm, MovePostForm
407 -from sphene.sphboard.models import Category, Post, PostAnnotation, ThreadInformation, Poll, PollChoice, PollVoters, POST_MARKUP_CHOICES, THREAD_TYPE_MOVED, THREAD_TYPE_DEFAULT, get_all_viewable_categories, ThreadLastVisit, CategoryLastVisit, PostKarmers
408 +from sphene.sphboard.models import Category, Post, PostAnnotation, ThreadInformation, Poll, PollChoice, PollVoters, POST_MARKUP_CHOICES, THREAD_TYPE_MOVED, THREAD_TYPE_DEFAULT, get_all_viewable_categories, ThreadLastVisit, CategoryLastVisit
411 def showCategory(request, group, category_id = None, showType = None, slug = None):
416 -def karmize(request, post_id, like):
417 - like = False if like=='0' else True
418 - postobj = Post.objects.get(id=post_id)
419 - if postobj._allow_karma():
420 - postobj.karma_touch(like)
421 - pk = PostKarmers(post = postobj, user = request.user, like = like)
423 - return HttpResponseRedirect( request.GET['next'] )
425 \ No newline at end of file
427 [1]: http://rtime.felk.cvut.cz/osp/student/belesjak/formula.png
428 [2]: http://rtime.felk.cvut.cz/osp/student/belesjak/InverseTrig84.gif
429 [3]: http://rtime.felk.cvut.cz/osp/student/belesjak/screenshot.png