[[!meta title="Beleščák Jakub"]] **Název projektu:** Sphene Community Tools **Domovská stránka projektu:** Zadání ====== 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". Kazdy prispevek ve foru dostane 2 tlacitka pro libi/nelibi. Bude se pocitat hodnoceni jednotlivych prispevku a rovnez hodnoceni uzivatelu. Hodnoceni uzivatelu bude zhodnocovat: - Dobu po kterou jsou zaregistrovani/neaktivni - Pocet prispevku - Hodnoceni vlastnich prispevku Hodnoceni nesmi prilis favorizovat nektere skupiny uzivatelu (jiz dlouho registrovane...) Výsledné řešení =============== 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. Rovnice pro výpočet je následující ![Rovnice][1] (arctan vypadá asi takhle :-), ![alt text][2] čímž je docíleno, že k nejhorším/nejlepším karmám se člověk blíží mnohem pomaleji, než kolem karmy neutrální. Výsledek pak vypadá asi takto ![alt text][3] Zhodnocení ========== 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. Prezentace ========== * [[Prezentace záměru práce|sct.odp]] * [[Prezentace výdledku práce|sct_vysledek.odp]] Ohloh ===== Ohloh profile for Jakub Beleščák Přílohy ======= Moje komunikace: Subject: Re: sct cooperation From: Herbert Poul Date: Fri, 12 Mar 2010 10:03:48 +0200 To: Jakub Beles(c(ák Hi Jakub, the diff looks ok, i will merge it when i have some more time thanks, herbert > > On Thu, Mar 11, 2010 at 12:44 PM, Jakub Beles(c(ák > > wrote: > > gr8, > > well the formula is still being tested and is discussed a lot, but it > > always > > depends on the concrete community , its size etc... > > > > I was thinking of making it optional, but that would require for the > > settings > > values to be accessible in templates, through which is the karma and > > its controls displayed. > > It would be really gr8, to take karma to account to the heat calculation, > > but I can not decypher, how this "call function by its name stored in > > settings" > > is done. > > > > Have a nice weekend > > > > Jakub > > > > Herbert Poul wrote: >> >> Hi Jakub, >> >> >> >> i am very glad to hear that you want to contribute part of your work >> >> back. If you like we can further discuss the features (although i >> >> guess since you know more about your requirements and have thought >> >> more deeply about it i can't necessary comment much about your >> >> suggested formular :) but if you need help for how to integrate it >> >> into SCT code i might be able to give a hint.. ) >> >> for me the best way would be if you could send me a diff (ie. patch) >> >> to 0.6 - if you like i could even give you write permission to the SVN >> >> repository so you can develop this in a SCT branch (if you don't use >> >> your own SVN already).. >> >> >> >> to your idea: this sounds very good - for the periodic calculation: >> >> there is already a maintenance signal >> >> (sphene.community.signals.maintenance) which is used for the 'heat' >> >> calculation of forum threads - maybe it would make sense to use the >> >> same? (it is triggered with the 'sph_maintenance' management command) >> >> >> >> i'm looking forward to your changes ;) do you think it is possible to >> >> make this feature optional (ie. so it can be turned off using a >> >> SPH_SETTINGS option)? >> >> >> >> >> >> thanks, >> >> herbert >> >> >> >> On Mon, Feb 22, 2010 at 12:04 PM, Jakub Beles(c(ák >> >> wrote: >>> >>> Hello there, >>> >>> We've decided to use SCT as community portal for one small faculty. >>> >>> What >>> >>> they wanted, that wasn't in SCT is karma. I would like to >>> >>> participate in >>> >>> SCT development. So, what I can do now is: >>> >>> 1) Discuss what is the best way to implement it. >>> >>> 2) Send you changed version 0.6 >>> >>> 3) Send you diff from 0.6 >>> >>> 4) Forget about it, because it is completely useless >>> >>> >>> >>> Which one would you like? :-) >>> >>> >>> >>> >>> >>> My idea, and what I've partly implemented is: >>> >>> 1) posts have +,-. 1 user - 1 post - 1 vote >>> >>> 2) users have karma, which is >>> >>> a) precounted in database >>> >>> b) calculated with a function, which can be user overiden >>> >>> c) displayed with a template tag in interval -4,4 >>> >>> d) precounted periodically by management/command >>> >>> >>> >>> so far, my colleauge suggested this calculation function >>> >>> karma = 4 * 2/pi * arctan k*[ 0.03*(today-registered) + 0.01*num_posts >>> >>> - 0.03*(today-last_post) + sum{karma_post*(2*karma_author + 1)} ] >>> >>> the coefficients (4, 0.03, 0.01 and 0.03) will be put in SPH_SETTINGS >>> >>> >>> >>> Jakub Belescak >>> >>> >>> >>> PS: We are also considering integrating user2user messaging, perhaps >>> >>> instant user2user messaging. http://code.google.com/p/django-messages/ >>> >>> http://code.google.com/p/django-messaging/ >>> >>> >>> >>> >>> >>> >> >> >> >> > > > > Diff oproti verzi 0.6 Index: communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py =================================================================== --- communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy) +++ communitytools/sphenecoll/sphene/community/templatetags/sph_karma.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3) @@ -1,58 +0,0 @@ -from sphene.community.models import CommunityUserProfile -from django import template -register = template.Library() - -from sphene.sphboard.models import Post, PostKarmers -from django.contrib.auth.models import User -from django.conf import settings -import math -import datetime - -def calculate_karma_logic(community_user_profile): - 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'] - 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'] - 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'] - 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'] - us = community_user_profile - - age = (datetime.datetime.today() - us.user.date_joined).days - posts = Post.objects.filter(author=us.user,is_hidden=0).order_by('-postdate') - post_count = len(posts) - if post_count >0: - last_post = posts[0].postdate - else: - last_post = us.user.date_joined - inactivity = (datetime.datetime.today()-last_post).days - sumicka = 0 - for post1 in posts: - for pok in PostKarmers.objects.filter(post = post1): - if pok.like: - sumicka+= 2*pok.user.communityuserprofile_set.all()[0].karma+1 - else: - sumicka-= 2*pok.user.communityuserprofile_set.all()[0].karma+1 - return 2/math.pi * math.atan( karma_linearity_koeficient * ( karma_age_koeficient*age + karma_post_count_koeficient*post_count - karma_inactivity_koeficient*inactivity + sumicka ) ) - -def get_karma(community_user_profile): - if community_user_profile.karma_counted_date != community_user_profile.karma_counted_date.today( ): - community_user_profile.karma_counted_date = community_user_profile.karma_counted_date.today( ) - community_user_profile.karma = calculate_karma_logic(community_user_profile) - community_user_profile.save() - return community_user_profile.karma - -@register.simple_tag -def sph_user_karma(userv): -# return userv.id - try: - usr = CommunityUserProfile.objects.get( user__id = userv.id ) -# usr = userv.communityuserprofile_set.all()[0] - except CommunityUserProfile.DoesNotExist: - usr = CommunityUserProfile(user = userv) - usr.save() - try: - karma = round(4*get_karma(usr),2) - except : - karma = round(4*get_karma(usr),2) - if karma<=0: - return str(karma) - else: - return "+"+str(karma) Index: communitytools/sphenecoll/sphene/community/models.py =================================================================== --- communitytools/sphenecoll/sphene/community/models.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy) +++ communitytools/sphenecoll/sphene/community/models.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3) @@ -144,13 +144,6 @@ avatar_height = models.IntegerField(blank = True, null = True, ) avatar_width = models.IntegerField(blank = True, null = True, ) - karma = models.FloatField(default = 0, null = False, editable = False) - karma_counted_date = models.DateField(auto_now_add=True, editable = False) - def karma_counted(self): -# if self.karma_counted_date != self.karma_counted_date.today( ): -# self.karma = calculate_karma_logic(self) -# self.save() - return self.karma changelog = ( ( '2007-08-10 00', 'alter', 'ADD avatar varchar(100)' ), ( '2007-08-10 01', 'alter', 'ADD avatar_height integer' ), @@ -158,11 +151,6 @@ ( '2008-04-10 00', 'alter', 'ADD displayname varchar(250)' ), ( '2008-04-10 01', 'update', "SET displayname = ''" ), ( '2008-04-10 02', 'alter', 'ALTER displayname SET NOT NULL' ), - ( '2009-01-16 00', 'alter', 'ADD karma integer NOT NULL', ), - ( '2009-01-16 01', 'alter', 'ALTER COLUMN karma SET DEFAULT 0', ), - ( '2009-02-22 00', 'alter', 'ALTER COLUMN karma TYPE float', ), - ( '2009-02-22 01', 'update', 'SET karma = 0', ), - ( '2009-02-24 00', 'alter', 'ADD karma_counted_date date NOT NULL DEFAULT NOW()', ), ) class Meta: Index: communitytools/sphenecoll/sphene/sphboard/views.py =================================================================== --- communitytools/sphenecoll/sphene/sphboard/views.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy) +++ communitytools/sphenecoll/sphene/sphboard/views.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3) @@ -10,14 +10,13 @@ from sphene.community import PermissionDenied from sphene.community.middleware import get_current_sphdata from sphene.community.sphutils import sph_reverse, get_user_displayname, format_date, get_sph_setting, add_rss_feed, sph_render_to_response -from sphene.community.models import CommunityUserProfile from sphene.generic import advanced_object_list as objlist from sphene.sphboard.forms import PollForm, PollChoiceForm, PostForm, \ PostPollForm, PostAttachmentForm, \ AnnotateForm, MoveAndAnnotateForm, MovePostForm -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 +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 def showCategory(request, group, category_id = None, showType = None, slug = None): @@ -842,12 +841,3 @@ return str -def karmize(request, post_id, like): - like = False if like=='0' else True - postobj = Post.objects.get(id=post_id) - if postobj._allow_karma(): - postobj.karma_touch(like) - pk = PostKarmers(post = postobj, user = request.user, like = like) - pk.save() - return HttpResponseRedirect( request.GET['next'] ) - \ No newline at end of file Index: communitytools/sphenecoll/sphene/sphboard/models.py =================================================================== --- communitytools/sphenecoll/sphene/sphboard/models.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy) +++ communitytools/sphenecoll/sphene/sphboard/models.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3) @@ -2,7 +2,6 @@ from django.db import models from django.db.models import Q from django.db.models import signals -from django.db.models import Count from django.core.urlresolvers import reverse from django.core.mail import send_mass_mail from django.core.cache import cache @@ -566,42 +565,7 @@ # a custom category type might change this behavior tough by adding a # administration interface for hidden posts.) is_hidden = models.IntegerField(default = 0, editable = False, db_index = True ) - - karma = models.IntegerField(default = 0, null = False, editable = False) - #like increases karma, not like decreases - def karma_touch(self, like): - if like: - self.karma = self.karma+1 - else: - self.karma = self.karma-1 - self.save() - #whether the active user has already voted karma - def _allow_karma(self, user = None): - if user == None: user = get_current_user() - if not user or not user.is_authenticated() or user==self.author: - return False - else: - try: - pk = PostKarmers.objects.get(post = self, user = user) - return False - except PostKarmers.DoesNotExist: - return True - allow_karma = property(fget=_allow_karma) - #how has the current user voted - def _my_karma(self, user = None): - if user == None: user = get_current_user() - try: - pk = PostKarmers.objects.get(post = self, user = user) - return pk.like - except PostKarmers.DoesNotExist: - return None - my_karma = property(fget=_my_karma) - def _positive_karma(self): - return PostKarmers.objects.filter(post = self, like = True).aggregate(Count('id'))['id__count'] - positive_karma = property(fget=_positive_karma) - def _negative_karma(self): - return PostKarmers.objects.filter(post = self, like = False).aggregate(Count('id'))['id__count'] - negative_karma = property(fget=_negative_karma) + # allobjects also contain hidden posts. allobjects = models.Manager() # objects only contains non-hidden posts. @@ -612,8 +576,6 @@ ( '2008-01-06 00', 'alter', 'ADD is_hidden INTEGER', ), ( '2008-01-06 01', 'update', 'SET is_hidden = 0', ), ( '2008-01-06 02', 'alter', 'ALTER is_hidden SET NOT NULL', ), - ( '2009-01-16 00', 'alter', 'ADD karma integer NOT NULL', ), - ( '2009-01-16 01', 'alter', 'ALTER COLUMN karma SET DEFAULT 0', ), ) def is_sticky(self): @@ -740,7 +702,7 @@ if not user or not user.is_authenticated(): return False - if user.is_staff \ + if user.is_superuser \ or has_permission_flag( user, 'sphboard_hideallposts', self.category ): return True @@ -1470,12 +1432,6 @@ verbose_name = ugettext_lazy('Poll voter') verbose_name_plural = ugettext_lazy('Poll voters') -class PostKarmers(models.Model): - post = models.ForeignKey( Post, editable = False) - user = models.ForeignKey( User, editable = False) - like = models.BooleanField( editable = False) - class Meta: - unique_together = (( 'post', 'user' ),) class BoardUserProfile(models.Model): user = models.ForeignKey( User, unique = True) Index: communitytools/sphenecoll/sphene/sphboard/views.py =================================================================== --- communitytools/sphenecoll/sphene/sphboard/views.py (.../home/beda/prace/pythonws/socioportal/communitytools) (working copy) +++ communitytools/sphenecoll/sphene/sphboard/views.py (.../https://svn.toh.cz/socioportal/trunk/communitytools) (revision 3) @@ -10,14 +10,13 @@ from sphene.community import PermissionDenied from sphene.community.middleware import get_current_sphdata from sphene.community.sphutils import sph_reverse, get_user_displayname, format_date, get_sph_setting, add_rss_feed, sph_render_to_response -from sphene.community.models import CommunityUserProfile from sphene.generic import advanced_object_list as objlist from sphene.sphboard.forms import PollForm, PollChoiceForm, PostForm, \ PostPollForm, PostAttachmentForm, \ AnnotateForm, MoveAndAnnotateForm, MovePostForm -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 +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 def showCategory(request, group, category_id = None, showType = None, slug = None): @@ -842,12 +841,3 @@ return str -def karmize(request, post_id, like): - like = False if like=='0' else True - postobj = Post.objects.get(id=post_id) - if postobj._allow_karma(): - postobj.karma_touch(like) - pk = PostKarmers(post = postobj, user = request.user, like = like) - pk.save() - return HttpResponseRedirect( request.GET['next'] ) - \ No newline at end of file [1]: http://rtime.felk.cvut.cz/osp/student/belesjak/formula.png [2]: http://rtime.felk.cvut.cz/osp/student/belesjak/InverseTrig84.gif [3]: http://rtime.felk.cvut.cz/osp/student/belesjak/screenshot.png