Skip to content

Commit

Permalink
Proposals Review improvements (#3696)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoacierno authored Jan 28, 2024
1 parent b2b63f1 commit 1be1068
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 67 deletions.
38 changes: 20 additions & 18 deletions backend/reviews/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ def _review_proposals_recap_view(self, request, review_session):
raise PermissionDenied()

data = request.POST
mark_as_confirmed = data.get("mark_as_confirmed", False)

decisions = {
int(key.split("-")[1]): value
Expand All @@ -380,22 +379,17 @@ def _review_proposals_recap_view(self, request, review_session):
conference.submissions.filter(id__in=decisions.keys()).all()
)

field = "status" if mark_as_confirmed else "pending_status"

for proposal in proposals:
decision = decisions[proposal.id]

if decision == "accept":
setattr(proposal, field, Submission.STATUS.accepted)
proposal.status = Submission.STATUS.accepted
elif decision == "reject":
setattr(proposal, field, Submission.STATUS.rejected)

if mark_as_confirmed:
proposal.pending_status = ""
proposal.status = Submission.STATUS.rejected

Submission.objects.bulk_update(
proposals,
fields=[field, "pending_status"],
fields=["status"],
)

return redirect(
Expand All @@ -408,7 +402,9 @@ def _review_proposals_recap_view(self, request, review_session):
)

items = (
review_session.conference.submissions.annotate(
Submission.objects.for_conference(review_session.conference_id)
.non_cancelled()
.annotate(
score=Subquery(
UserReview.objects.select_related("score")
.filter(
Expand All @@ -428,6 +424,7 @@ def _review_proposals_recap_view(self, request, review_session):
"user", "score"
).filter(review_session_id=review_session_id),
),
"duration",
"audience_level",
"languages",
"speaker",
Expand Down Expand Up @@ -716,22 +713,27 @@ def get_next_to_review_item_id(

if review_session.is_proposals_review:
already_reviewed_ids = already_reviewed.values_list("proposal_id", flat=True)
allowed_tags = SubmissionTag.objects.exclude(id__in=exclude)
unvoted_item = (
review_session.conference.submissions.annotate(
skip_item_array = [skip_item] if skip_item else []
seen_items_to_ignore = list(already_reviewed_ids) + skip_item_array + seen
qs = (
Submission.objects.non_cancelled()
.for_conference(review_session.conference_id)
.annotate(
votes_received=Count(
"userreview",
filter=Q(userreview__review_session_id=review_session.id),
)
)
.exclude(
id__in=list(already_reviewed_ids) + [skip_item] + seen,
)
.order_by("votes_received", "?")
.filter(tags__in=allowed_tags)
.first()
)

if seen_items_to_ignore:
qs = qs.exclude(id__in=seen_items_to_ignore)

if exclude:
qs = qs.exclude(tags__in=exclude)

unvoted_item = qs.first()
elif review_session.is_grants_review:
already_reviewed_ids = already_reviewed.values_list("grant_id", flat=True)
unvoted_item = (
Expand Down
108 changes: 72 additions & 36 deletions backend/reviews/templates/proposal-review.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% extends "admin/base_site.html" %}
{% load i18n markdownify %}
{% load localize countryname %}
{% load localize countryname hostname %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
Expand All @@ -19,7 +19,8 @@

.review-row {
display: grid;
grid-template-columns: 100px 1fr;
grid-template-columns: 150px 1fr;
gap: 50px;
padding: 10px;
font-size: 13px;
border-bottom: 1px solid var(--hairline-color);
Expand Down Expand Up @@ -90,55 +91,59 @@
</script>

<fieldset class="module aligned">
<h2>Submission</h2>
<h2>Proposal</h2>

<div class="review-row">
<label>Languages</label>
<strong>Languages</strong>
<div>{{proposal.languages.all|join:", "}}</div>
</div>
<div class="review-row">
<label>Type</label>
<div>{{proposal.type}}</div>
<strong>Type</strong>
<div>{{proposal.type.name}}</div>
</div>
<div class="review-row">
<label>Audience Level</label>
<div>{{proposal.audience_level}}</div>
<strong>Duration</strong>
<div>{{proposal.duration.duration}} mins</div>
</div>
<div class="review-row">
<strong>Audience Level</strong>
<div>{{proposal.audience_level.name}}</div>
</div>
{% if has_english_language %}
<div class="review-row">
<label>Title [English]</label>
<strong>Title [English]</strong>
<div>{{proposal.title | localize:"en" }}</div>
</div>
<div class="review-row">
<label>Elevator Pitch [English]</label>
<div>{{proposal.elevator_pitch | localize:"en" | markdownify}}</div>
<strong>Elevator Pitch [English]</strong>
<div>{{proposal.elevator_pitch | localize:"en" | markdownify | linebreaksbr}}</div>
</div>
<div class="review-row">
<label>Abstract [English]</label>
<div>{{proposal.abstract | localize:"en" | markdownify}}</div>
<strong>Abstract [English]</strong>
<div>{{proposal.abstract | localize:"en" | markdownify | linebreaksbr}}</div>
</div>
{% endif %}
{% if has_italian_language %}
<div class="review-row">
<label>Title [Italian]</label>
<strong>Title [Italian]</strong>
<div>{{proposal.title | localize:"it" }}</div>
</div>

<div class="review-row">
<label>Elevator Pitch [Italian]</label>
<div>{{proposal.elevator_pitch | localize:"it" | markdownify}}</div>
<strong>Elevator Pitch [Italian]</strong>
<div>{{proposal.elevator_pitch | localize:"it" | markdownify | linebreaksbr}}</div>
</div>
<div class="review-row">
<label>Abstract [Italian]</label>
<div>{{proposal.abstract | localize:"it" | markdownify}}</div>
<strong>Abstract [Italian]</strong>
<div>{{proposal.abstract | localize:"it" | markdownify | linebreaksbr}}</div>
</div>
{% endif %}
<div class="review-row">
<label>Notes</label>
<strong>Notes</strong>
<div>{{proposal.notes}}</div>
</div>
<div class="review-row">
<label>Community Voting</label>
<strong>Community Voting</strong>
<div>
<ul>
{% for ranking in proposal.rankings.all %}
Expand All @@ -150,7 +155,7 @@ <h2>Submission</h2>
</div>
</div>
<div class="review-row">
<label>Voted by</label>
<strong>Voted by</strong>
<div>
<details>
<summary>See votes ({{proposal.userreview_set.count}} votes)</summary>
Expand All @@ -170,41 +175,47 @@ <h2>Submission</h2>
</div>
</div>
<div class="review-row">
<label>Open submission</label>
<strong>Open proposal [Website]</strong>
<div>
<a target="_blank" href="https://pycon.it/submission/{{proposal.hashid}}">Open Proposal in website</a>
</div>
</div>
<div class="review-row">
<strong>Open proposal [Admin]</strong>
<div>
<a target="_blank" href="{% url 'admin:submissions_submission_change' object_id=proposal.id %}">Open Submission</a>
<a target="_blank" href="{% url 'admin:submissions_submission_change' object_id=proposal.id %}">Open Proposal in admin</a>
</div>
</div>
</fieldset>
<fieldset class="module aligned">
<h2>Speaker</h2>
<div class="review-row">
<label>Name</label>
<strong>Name</strong>
<div>{{speaker.fullname}}</div>
</div>
<div class="review-row">
<label>Country</label>
<strong>Country</strong>
<div>{{speaker.country | countryname}}</div>
</div>
<div class="review-row">
<label>Speaker Experience</label>
<div>{{participant.speaker_level}}</div>
<strong>Speaker Experience</strong>
<div>{{participant.get_speaker_level_display}}</div>
</div>
<div class="review-row">
<label>Previous Talk Video</label>
<strong>Previous Talk Video</strong>
<div>
{% if participant.previous_talk_video %}
<a target="_blank" rel="noreferrer noopener" href="{{participant.previous_talk_video}}">
{{participant.previous_talk_video}}
</a>
{% endif%}
{% endif %}
</div>
</div>
<div class="review-row">
<label>Requested a grant?</label>
<strong>Requested a grant?</strong>
{% if grant %}
<div>
<a target="_blank" href="{{grant_link}}">Yes, open grant</a>
<a target="_blank" href="{{grant_link}}">Yes, open grant in admin</a>
</div>
{% else %}
<div>
Expand All @@ -216,18 +227,43 @@ <h2>Speaker</h2>
<div class="review-row">
<label>Grant status</label>
<div>
{{grant.status}}
{{grant.get_status_display}}
</div>
</div>
{% endif %}
<div class="review-row">
<label>Bio</label>
<div>{{participant.bio}}</div>
<strong>Bio</strong>
<div>{{participant.bio|linebreaksbr}}</div>
</div>
<div class="review-row">
<label>Participant Info</label>
<strong>Participant Info</strong>
<div>
<a href="{% url 'admin:participants_participant_change' participant.id %}" target="_blank">Open Participant</a>
<a href="{% url 'admin:participants_participant_change' participant.id %}" target="_blank">Open Participant in admin</a>
</div>
</div>
<div class="review-row">
<strong>Socials</strong>
<div>
<ul>
{% if participant.website %}
<li><a target="_blank" rel="noopener noreferrer" href="{{participant.website}}">Website ({{participant.website|hostname}})</a></li>
{% endif %}
{% if participant.twitter_handle %}
<li><a target="_blank" rel="noopener noreferrer" href="https://twitter.com/@{{participant.twitter_handle}}">Twitter ({{participant.twitter_handle}})</a></li>
{% endif %}
{% if participant.instagram_handle %}
<li><a target="_blank" rel="noopener noreferrer" href="https://instagram.com/{{participant.instagram_handle}}">Instagram ({{participant.instagram_handle}})</a></li>
{% endif %}
{% if participant.linkedin_url %}
<li><a target="_blank" rel="noopener noreferrer" href="{{participant.linkedin_url}}">Linkedin</a></li>
{% endif %}
{% if participant.facebook_url %}
<li><a target="_blank" rel="noopener noreferrer" href="{{participant.facebook_url}}">Facebook</a></li>
{% endif %}
{% if participant.mastodon_handle %}
<li><a target="_blank" rel="noopener noreferrer" href="{{participant.mastodon_handle}}">Mastodon ({{participant.mastodon_handle}})</a></li>
{% endif %}
</ul>
</div>
</div>
</fieldset>
Expand Down
14 changes: 4 additions & 10 deletions backend/reviews/templates/proposals-recap.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@
}

.reviews-bottom-bar-confirm-bar {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
max-width: 800px;
}

Expand Down Expand Up @@ -368,12 +364,10 @@ <h3>Show proposals with N reviews:</h3>
<div id="content" class="reviews-bottom-bar colM">
<div class="reviews-bottom-bar-content">
<div class="submit-row reviews-bottom-bar-confirm-bar">
<label>
<input type="checkbox" name="mark_as_confirmed" />
<span>By default your choice is marked as pending so you can review and
confirm them later. Check here if you want to apply the
accept/reject immediately.</span>
</label>
<p>
Once done, click the button to save your choices.
You can change as many times as you need, no emails will be sent.
</p>
<input value="Submit choices" type="submit" />
</div>
<div class="reviews-bottom-bar-stats">
Expand Down
11 changes: 11 additions & 0 deletions backend/reviews/templatetags/hostname.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from urllib.parse import urlparse

from django import template

register = template.Library()


@register.filter
def hostname(url: str):
parsed_url = urlparse(url)
return parsed_url.netloc
37 changes: 37 additions & 0 deletions backend/reviews/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,43 @@ def test_next_item_to_review_prefers_items_with_fewer_votes():
assert next_to_review == submission_2.id


@pytest.mark.parametrize("iteration", range(10))
def test_next_item_to_review_for_submissions_ignores_excluded_tags(iteration):
tag_1 = SubmissionTagFactory(name="A")
tag_2 = SubmissionTagFactory(name="B")
tag_3 = SubmissionTagFactory(name="C")

user_1 = UserFactory(is_staff=True, is_superuser=True)

conference = ConferenceFactory()
conference_2 = ConferenceFactory()

SubmissionFactory(conference=conference_2)
SubmissionFactory(conference=conference_2)
SubmissionFactory(conference=conference_2)

review_session = ReviewSessionFactory(
conference=conference,
session_type=ReviewSession.SessionType.PROPOSALS,
)
AvailableScoreOptionFactory(review_session=review_session, numeric_value=0)
AvailableScoreOptionFactory(review_session=review_session, numeric_value=1)

submission_1 = SubmissionFactory(conference=conference)
submission_1.tags.add(tag_1)
submission_1.tags.add(tag_3)

submission_2 = SubmissionFactory(conference=conference)
submission_2.tags.add(tag_1)
submission_2.tags.add(tag_2)
submission_2.tags.add(tag_3)

next_to_review = get_next_to_review_item_id(
review_session, user_1, exclude=[tag_2.id]
)
assert next_to_review == submission_1.id


@pytest.mark.parametrize(
"scores, avg",
[
Expand Down
Loading

1 comment on commit 1be1068

@vercel
Copy link

@vercel vercel bot commented on 1be1068 Jan 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.