Merge pull request #20 from lca2017/chrisjrn/019-track-abstains-properly
Abstains are no longer tracked as "votes"
This commit is contained in:
commit
c8c25718bc
2 changed files with 28 additions and 53 deletions
|
@ -8,6 +8,7 @@ from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q, F
|
from django.db.models import Q, F
|
||||||
from django.db.models import Case, When, Value
|
from django.db.models import Case, When, Value
|
||||||
|
from django.db.models import Count
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
@ -23,11 +24,11 @@ def score_expression():
|
||||||
(2 * F("plus_two") + F("plus_one")) -
|
(2 * F("plus_two") + F("plus_one")) -
|
||||||
(F("minus_one") + 2 * F("minus_two"))
|
(F("minus_one") + 2 * F("minus_two"))
|
||||||
) / (
|
) / (
|
||||||
F("vote_count") - F("abstain") * 1.0
|
F("vote_count") * 1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
return Case(
|
return Case(
|
||||||
When(vote_count=F("abstain"), then=Value("0")), # no divide by zero
|
When(vote_count=0, then=Value("0")), # no divide by zero
|
||||||
default=score,
|
default=score,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -246,6 +247,7 @@ class ProposalResult(models.Model):
|
||||||
proposal = models.OneToOneField(ProposalBase, related_name="result", verbose_name=_("Proposal"))
|
proposal = models.OneToOneField(ProposalBase, related_name="result", verbose_name=_("Proposal"))
|
||||||
score = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"), verbose_name=_("Score"))
|
score = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"), verbose_name=_("Score"))
|
||||||
comment_count = models.PositiveIntegerField(default=0, verbose_name=_("Comment count"))
|
comment_count = models.PositiveIntegerField(default=0, verbose_name=_("Comment count"))
|
||||||
|
# vote_count only counts non-abstain votes.
|
||||||
vote_count = models.PositiveIntegerField(default=0, verbose_name=_("Vote count"))
|
vote_count = models.PositiveIntegerField(default=0, verbose_name=_("Vote count"))
|
||||||
abstain = models.PositiveIntegerField(default=0, verbose_name=_("Abstain"))
|
abstain = models.PositiveIntegerField(default=0, verbose_name=_("Abstain"))
|
||||||
plus_two = models.PositiveIntegerField(default=0, verbose_name=_("Plus two"))
|
plus_two = models.PositiveIntegerField(default=0, verbose_name=_("Plus two"))
|
||||||
|
@ -268,57 +270,30 @@ class ProposalResult(models.Model):
|
||||||
def full_calculate(cls):
|
def full_calculate(cls):
|
||||||
for proposal in ProposalBase.objects.all():
|
for proposal in ProposalBase.objects.all():
|
||||||
result, created = cls._default_manager.get_or_create(proposal=proposal)
|
result, created = cls._default_manager.get_or_create(proposal=proposal)
|
||||||
result.comment_count = Review.objects.filter(proposal=proposal).count()
|
result.update_vote()
|
||||||
result.vote_count = LatestVote.objects.filter(proposal=proposal).count()
|
|
||||||
result.abstain = LatestVote.objects.filter(
|
|
||||||
proposal=proposal,
|
|
||||||
vote=VOTES.ABSTAIN,
|
|
||||||
).count()
|
|
||||||
result.plus_two = LatestVote.objects.filter(
|
|
||||||
proposal=proposal,
|
|
||||||
vote=VOTES.PLUS_TWO
|
|
||||||
).count()
|
|
||||||
result.plus_one = LatestVote.objects.filter(
|
|
||||||
proposal=proposal,
|
|
||||||
vote=VOTES.PLUS_ONE
|
|
||||||
).count()
|
|
||||||
result.minus_one = LatestVote.objects.filter(
|
|
||||||
proposal=proposal,
|
|
||||||
vote=VOTES.MINUS_ONE
|
|
||||||
).count()
|
|
||||||
result.minus_two = LatestVote.objects.filter(
|
|
||||||
proposal=proposal,
|
|
||||||
vote=VOTES.MINUS_TWO
|
|
||||||
).count()
|
|
||||||
result.save()
|
|
||||||
cls._default_manager.filter(pk=result.pk).update(score=score_expression())
|
|
||||||
|
|
||||||
def update_vote(self, vote, previous=None, removal=False):
|
def update_vote(self, *a, **k):
|
||||||
mapping = {
|
proposal = self.proposal
|
||||||
VOTES.ABSTAIN: "abstain",
|
self.comment_count = Review.objects.filter(proposal=proposal).count()
|
||||||
VOTES.PLUS_TWO: "plus_two",
|
agg = LatestVote.objects.filter(proposal=proposal).values(
|
||||||
VOTES.PLUS_ONE: "plus_one",
|
"vote"
|
||||||
VOTES.MINUS_ONE: "minus_one",
|
).annotate(
|
||||||
VOTES.MINUS_TWO: "minus_two",
|
count=Count("vote")
|
||||||
}
|
)
|
||||||
if previous:
|
vote_count = {}
|
||||||
if previous == vote:
|
# Set the defaults
|
||||||
return
|
for option in VOTES.CHOICES:
|
||||||
if removal:
|
vote_count[option[0]] = 0
|
||||||
setattr(self, mapping[previous], models.F(mapping[previous]) + 1)
|
# Set the actual values if present
|
||||||
else:
|
for d in agg:
|
||||||
setattr(self, mapping[previous], models.F(mapping[previous]) - 1)
|
vote_count[d["vote"]] = d["count"]
|
||||||
else:
|
|
||||||
if removal:
|
self.abstain = vote_count[VOTES.ABSTAIN]
|
||||||
self.vote_count = models.F("vote_count") - 1
|
self.plus_two = vote_count[VOTES.PLUS_TWO]
|
||||||
else:
|
self.plus_one = vote_count[VOTES.PLUS_ONE]
|
||||||
self.vote_count = models.F("vote_count") + 1
|
self.minus_one = vote_count[VOTES.MINUS_ONE]
|
||||||
if removal:
|
self.minus_two = vote_count[VOTES.MINUS_TWO]
|
||||||
setattr(self, mapping[vote], models.F(mapping[vote]) - 1)
|
self.vote_count = sum(i[1] for i in vote_count.items()) - self.abstain
|
||||||
self.comment_count = models.F("comment_count") - 1
|
|
||||||
else:
|
|
||||||
setattr(self, mapping[vote], models.F(mapping[vote]) + 1)
|
|
||||||
self.comment_count = models.F("comment_count") + 1
|
|
||||||
self.save()
|
self.save()
|
||||||
model = self.__class__
|
model = self.__class__
|
||||||
model._default_manager.filter(pk=self.pk).update(score=score_expression())
|
model._default_manager.filter(pk=self.pk).update(score=score_expression())
|
||||||
|
|
|
@ -292,7 +292,7 @@ def review_detail(request, pk):
|
||||||
@require_POST
|
@require_POST
|
||||||
def review_delete(request, pk):
|
def review_delete(request, pk):
|
||||||
review = get_object_or_404(Review, pk=pk)
|
review = get_object_or_404(Review, pk=pk)
|
||||||
section_slug = review.section.slug
|
section_slug = review.section
|
||||||
|
|
||||||
if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
|
if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
|
||||||
return access_not_permitted(request)
|
return access_not_permitted(request)
|
||||||
|
|
Loading…
Reference in a new issue