Skip to content

Commit 23e5162

Browse files
committed
Merge branch 'master' into voting
2 parents a9f2859 + 0fec158 commit 23e5162

File tree

13 files changed

+129
-53
lines changed

13 files changed

+129
-53
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ sudo: false
33
python:
44
- '2.7'
55
- '3.4'
6+
- '3.5'
67
addons:
78
postgresql: "9.3"
89

@@ -11,7 +12,8 @@ cache:
1112
- $HOME/.pip-cache/
1213

1314
install:
14-
- pip install -r requirements-dev.txt --allow-all-external --download-cache $HOME/.pip-cache
15+
- pip install pip==8.1.2
16+
- pip install -r requirements-dev.txt --allow-all-external
1517
- pip install coveralls
1618

1719
env:

junction/base/constants.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ class ProposalReviewerComment:
8080
class ProposalVotesFilter:
8181
_NO_VOTES = [0, "No votes"]
8282
_MIN_ONE_VOTE = [1, "Minimum 1 vote"]
83-
_SORT = [2, "Sort by vote value"]
83+
_SORT_BY_SUM = [2, "Sort by total votes"]
84+
_SORT_BY_REVIEWER = [3, "Sort by your votes"]
85+
_SORT_BY_SELECTION = [4, "Sort by selection"]
8486

8587

8688
class ConferenceSettingConstants:

junction/feedback/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def get_choice_feedback_questions(conference_id):
5656
schedule item type.
5757
"""
5858
qs = ChoiceFeedbackQuestion.objects.filter(
59-
conference_id=conference_id).select_related('allowed_values')
59+
conference_id=conference_id).prefetch_related('allowed_values')
6060
return _get_question_oragnized_by_type(qs)
6161

6262

junction/proposals/dashboard.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@
2626

2727
from .forms import ProposalVotesFilterForm
2828
from .permissions import is_conference_moderator
29-
29+
from .utils import _sort_proposals_for_dashboard
3030
from .models import (
3131
Proposal,
3232
ProposalComment,
33-
ProposalSection,
3433
ProposalSectionReviewer,
3534
ProposalSectionReviewerVoteValue
3635
)
@@ -194,7 +193,7 @@ def reviewer_comments_dashboard(request, conference_slug):
194193
@require_http_methods(['GET', 'POST'])
195194
def reviewer_votes_dashboard(request, conference_slug):
196195
conference = get_object_or_404(Conference, slug=conference_slug)
197-
196+
user = request.user
198197
if not is_conference_moderator(user=request.user, conference=conference):
199198
raise PermissionDenied
200199

@@ -227,36 +226,8 @@ def reviewer_votes_dashboard(request, conference_slug):
227226
'errors': form.errors})
228227

229228
# Valid form
230-
cps = form.cleaned_data['proposal_section']
231-
cpt = form.cleaned_data['proposal_type']
232-
votes = form.cleaned_data['votes']
233-
review_status = form.cleaned_data['review_status']
234-
proposal_sections = conference.proposal_sections.all()
235-
236-
if cps != 'all':
237-
proposal_sections = ProposalSection.objects.filter(pk=cps)
238-
if cpt != 'all':
239-
proposals_qs = proposals_qs.filter(proposal_type__id__in=cpt)
240-
if votes != 'all':
241-
votes = int(votes)
242-
if review_status != 'all':
243-
proposals_qs = proposals_qs.filter(review_status=review_status)
244229

245-
if votes == ProposalVotesFilter.NO_VOTES:
246-
proposals_qs = [
247-
p for p in proposals_qs if p.get_reviewer_votes_count() == votes]
248-
elif votes == ProposalVotesFilter.MIN_ONE_VOTE:
249-
proposals_qs = [
250-
p for p in proposals_qs if p.get_reviewer_votes_count() >= votes]
251-
elif votes == ProposalVotesFilter.SORT:
252-
proposals_qs = sorted(
253-
proposals_qs, key=lambda x: x.get_reviewer_votes_sum(),
254-
reverse=True)
255-
256-
for section in proposal_sections:
257-
section_proposals = [
258-
p for p in proposals_qs if p.proposal_section == section]
259-
proposals.append(s_items(section, section_proposals))
230+
proposals = _sort_proposals_for_dashboard(conference, proposals_qs, user, form)
260231

261232
return render(request, 'proposals/votes-dashboard.html',
262233
{'conference': conference,

junction/proposals/models.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import, unicode_literals
33

4-
# Standard Library
54
from datetime import datetime
65

7-
# Third Party Stuff
86
from django.contrib.auth.models import User
97
from django.core.urlresolvers import reverse
108
from django.db import models
@@ -14,11 +12,9 @@
1412
from hashids import Hashids
1513
from simple_history.models import HistoricalRecords
1614

17-
# Junction Stuff
18-
from junction.base.constants import (
19-
ProposalReviewStatus, ProposalStatus, ProposalTargetAudience, ProposalUserVoteRole,
20-
PSRVotePhase, ProposalCommentType,
21-
)
15+
from junction.base.constants import PSRVotePhase, ProposalCommentType, \
16+
ProposalReviewStatus, ProposalReviewVote, ProposalStatus, \
17+
ProposalTargetAudience, ProposalUserVoteRole
2218
from junction.base.models import AuditModel, TimeAuditModel
2319
from junction.conferences.models import Conference, ConferenceProposalReviewer
2420

@@ -184,7 +180,7 @@ def get_reviewer_votes_count(self):
184180
def get_reviewer_votes_count_by_value(self, vote_value):
185181
""" Show sum of reviewer votes for given vote value. """
186182
return ProposalSectionReviewerVote.objects.filter(
187-
proposal=self, vote_value=vote_value
183+
proposal=self, vote_value__vote_value=vote_value
188184
).count()
189185

190186
def get_reviewer_votes_sum(self):
@@ -194,12 +190,27 @@ def get_reviewer_votes_sum(self):
194190
sum_of_votes = sum((v.vote_value.vote_value for v in votes))
195191
return sum_of_votes
196192

193+
def get_reviewer_vote_value(self, reviewer):
194+
try:
195+
vote = ProposalSectionReviewerVote.objects.get(
196+
proposal=self, voter__conference_reviewer__reviewer=reviewer,
197+
)
198+
return vote.vote_value.vote_value
199+
except ProposalSectionReviewerVote.DoesNotExist:
200+
return 0
201+
197202
def get_reviewers_count(self):
198203
""" Count of reviewers for given proposal section """
199204
return ProposalSectionReviewer.objects.filter(
200205
proposal_section=self.proposal_section
201206
).count()
202207

208+
def has_negative_votes(self):
209+
""" Show sum of reviewer votes for given vote value. """
210+
return ProposalSectionReviewerVote.objects.filter(
211+
proposal=self, vote_value__vote_value=ProposalReviewVote.NOT_ALLOWED,
212+
).count() > 0
213+
203214
class Meta:
204215
unique_together = ("conference", "slug")
205216

junction/proposals/templatetags/proposal_filters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def get_reviewers_vote_details(proposal, user):
7979

8080
vc_qs = ProposalComment.objects.filter(
8181
proposal=proposal,
82-
commenter=reviewer,
82+
commenter=reviewer.conference_reviewer.reviewer,
8383
vote=True)
8484
if vc_qs:
8585
vote_comment = vc_qs[0].comment

junction/proposals/utils.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import collections
2+
13
from django.core.exceptions import PermissionDenied
24

3-
from junction.base.constants import PSRVotePhase, ProposalCommentType
5+
from junction.base.constants import PSRVotePhase, ProposalCommentType, \
6+
ProposalReviewVote, ProposalVotesFilter
47
from junction.proposals import permissions
5-
from junction.proposals.models import ProposalComment, ProposalSectionReviewer, \
6-
ProposalSectionReviewerVote, ProposalSectionReviewerVoteValue
8+
from junction.proposals.models import ProposalComment, ProposalSection, \
9+
ProposalSectionReviewer, ProposalSectionReviewerVote, \
10+
ProposalSectionReviewerVoteValue
711

812

913
def get_reviewer_vote_info(user, conference, proposal, vote_phase):
@@ -67,3 +71,81 @@ def update_reviewer_vote_info(user, psr_vote, vote_value, comment, phase, propos
6771
)
6872

6973
return psr_vote, p_comment
74+
75+
76+
def _sort_proposals_for_dashboard(conference, proposals_qs, user, form):
77+
"""
78+
"""
79+
cps = form.cleaned_data['proposal_section']
80+
cpt = form.cleaned_data['proposal_type']
81+
votes = form.cleaned_data['votes']
82+
review_status = form.cleaned_data['review_status']
83+
84+
proposal_sections = conference.proposal_sections.all()
85+
s_items = collections.namedtuple('section_items', 'section proposals')
86+
proposals = []
87+
88+
if cps != 'all':
89+
proposal_sections = ProposalSection.objects.filter(pk=cps)
90+
if cpt != 'all':
91+
proposals_qs = proposals_qs.filter(proposal_type__id__in=cpt)
92+
if votes != 'all':
93+
votes = int(votes)
94+
if review_status != 'all':
95+
proposals_qs = proposals_qs.filter(review_status=review_status)
96+
97+
if votes == ProposalVotesFilter.NO_VOTES:
98+
proposals_qs = [
99+
p for p in proposals_qs if p.get_reviewer_votes_count() == votes]
100+
elif votes == ProposalVotesFilter.MIN_ONE_VOTE:
101+
proposals_qs = [
102+
p for p in proposals_qs if p.get_reviewer_votes_count() >= votes]
103+
elif votes == ProposalVotesFilter.SORT_BY_REVIEWER:
104+
proposals_qs = sorted(
105+
proposals_qs,
106+
key=lambda x: x.get_reviewer_vote_value(reviewer=user),
107+
reverse=True,
108+
)
109+
elif votes == ProposalVotesFilter.SORT_BY_SUM:
110+
proposals_qs = sorted(
111+
proposals_qs, key=lambda x: x.get_reviewer_votes_sum(),
112+
reverse=True)
113+
proposals = [s_items('', proposals_qs)]
114+
115+
elif votes == ProposalVotesFilter.SORT_BY_SELECTION:
116+
# Selection of proposal is based on conference guidelines.
117+
# More info is available at http://tiny.cc/qzo5cy
118+
119+
proposals_qs = [p for p in proposals_qs if not p.has_negative_votes()]
120+
proposals_qs = sorted(proposals_qs, key=lambda x: x.get_reviewer_votes_sum(), reverse=True)
121+
122+
selected = [p for p in proposals_qs if p.get_reviewer_votes_count_by_value(ProposalReviewVote.MUST_HAVE) >= 2]
123+
proposals.append(s_items('Selected', selected))
124+
125+
batch1 = [p for p in proposals_qs
126+
if p.get_reviewer_votes_count_by_value(ProposalReviewVote.MUST_HAVE) == 1 and
127+
p.get_reviewer_votes_count_by_value(ProposalReviewVote.GOOD) > 2]
128+
proposals.append(s_items('1 Must Have & 2+ Good Votes', batch1))
129+
130+
batch2 = [p for p in proposals_qs
131+
if p.get_reviewer_votes_count_by_value(ProposalReviewVote.MUST_HAVE) == 1 and
132+
p.get_reviewer_votes_count_by_value(ProposalReviewVote.GOOD) == 1]
133+
proposals.append(s_items('1 Must Have & 1 Good Vote', batch2))
134+
135+
batch3 = [p for p in proposals_qs
136+
if p.get_reviewer_votes_count_by_value(ProposalReviewVote.GOOD) > 2 and
137+
p not in batch1]
138+
proposals.append(s_items('2+ Good Votes', batch3))
139+
140+
batch4 = [p for p in proposals_qs
141+
if p.get_reviewer_votes_count_by_value(ProposalReviewVote.GOOD) == 1 and
142+
p.get_reviewer_votes_count_by_value(ProposalReviewVote.NOT_BAD) > 2 and
143+
p not in batch2]
144+
proposals.append(s_items('1 Good & 2+ Not Bad votes', batch4))
145+
146+
if votes not in (ProposalVotesFilter.SORT_BY_SUM, ProposalVotesFilter.SORT_BY_SELECTION):
147+
for section in proposal_sections:
148+
section_proposals = [p for p in proposals_qs if p.proposal_section == section]
149+
proposals.append(s_items(section, section_proposals))
150+
151+
return proposals

junction/proposals/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ def proposals_to_review(request, conference_slug):
285285

286286
context = {
287287
'proposals_to_review': proposals_to_review,
288+
'proposal_reviewer_sections': proposal_reviewer_sections,
288289
'proposal_sections': proposal_sections,
289290
'proposal_types': proposal_types,
290291
'conference': conference,

junction/templates/proposals/email/comment/message.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ <h1 style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;color:#
1414
There is a new <b>{{comment_type}}</b> comment on
1515
"<a href="{{host}}{{proposal.get_absolute_url}}" style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6em;margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;color:#348eda;" >{{proposal.title}}</a>"
1616
by <b style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6em;margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;" >
17-
{% if comment.private %}
17+
{% if comment.private or comment.reviewer %}
1818
{% if by_author %}
1919
Author
2020
{% else %}

junction/templates/proposals/email/comment/message.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
There is a new comment on {{host}}{{proposal.get_absolute_url}} by {% if comment.private %}one of the reviewer{% else %}*{{comment.commenter}}*{% endif %}:
1+
There is a new comment on {{host}}{{proposal.get_absolute_url}} by {% if comment.private or comment.reviewer %}{% if by_author %}Author{% else %}{{comment.get_reviewer_nick}}{% endif %}{% else %}{{comment.commenter}}{% endif %}:
22

33
"{{comment.comment}}"
44

0 commit comments

Comments
 (0)