Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 8 commits
  • 10 files changed
  • 0 commit comments
  • 1 contributor
View
32 .gondor/error_pages/503.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8" />
+ <title>PyCon &mdash; Maintenance</title>
+ <style>
+ body {
+ font-family: sans-serif;
+ color: #444;
+ font-size: 120%;
+ }
+ h1 {
+ font-weight: 400;
+ }
+ .container {
+ width: 940px;
+ margin: auto 50px;
+ zoom: 1;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <h1>PyCon US 2013</h1>
+
+ <p>
+ We're currently deploying a new version of the site.<br />
+ Refresh in a few seconds and you might be the lucky first visitor.
+ </p>
+ </div>
+ </body>
+</html>
View
20 symposion/proposals/models.py
@@ -5,9 +5,12 @@
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import Q
+from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
+import reversion
+
from markitup.fields import MarkupField
from model_utils.managers import InheritanceManager
@@ -55,7 +58,7 @@ class ProposalKind(models.Model):
section = models.ForeignKey(Section, related_name="proposal_kinds")
- name = models.CharField("name", max_length=100)
+ name = models.CharField(_("Name"), max_length=100)
slug = models.SlugField()
def __unicode__(self):
@@ -70,15 +73,17 @@ class ProposalBase(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(
+ _("Brief Outline"),
max_length=400, # @@@ need to enforce 400 in UI
help_text="If your talk is accepted this will be made public and printed in the program. Should be one paragraph, maximum 400 characters."
)
abstract = MarkupField(
- help_text="Detailed description and outline. Will be made public if your talk is accepted. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/' target='_blank'>Markdown</a>."
+ _("Detailed Abstract"),
+ help_text=_("Detailed description and outline. Will be made public if your talk is accepted. Edit using <a href='http://daringfireball.net/projects/markdown/basics' target='_blank'>Markdown</a>.")
)
additional_notes = MarkupField(
blank=True,
- help_text="Anything else you'd like the program committee to know when making their selection: your past speaking experience, open source community experience, etc. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/' target='_blank'>Markdown</a>."
+ help_text=_("Anything else you'd like the program committee to know when making their selection: your past speaking experience, open source community experience, etc. Edit using <a href='http://daringfireball.net/projects/markdown/basics' target='_blank'>Markdown</a>.")
)
submitted = models.DateTimeField(
default=datetime.datetime.now,
@@ -105,6 +110,9 @@ def speakers(self):
yield speaker
+reversion.register(ProposalBase)
+
+
class AdditionalSpeaker(models.Model):
SPEAKING_STATUS_PENDING = 1
@@ -112,9 +120,9 @@ class AdditionalSpeaker(models.Model):
SPEAKING_STATUS_DECLINED = 3
SPEAKING_STATUS = [
- (SPEAKING_STATUS_PENDING, "Pending"),
- (SPEAKING_STATUS_ACCEPTED, "Accepted"),
- (SPEAKING_STATUS_DECLINED, "Declined"),
+ (SPEAKING_STATUS_PENDING, _("Pending")),
+ (SPEAKING_STATUS_ACCEPTED, _("Accepted")),
+ (SPEAKING_STATUS_DECLINED, _("Declined")),
]
speaker = models.ForeignKey("speakers.Speaker")
View
47 symposion/proposals/views.py
@@ -10,6 +10,7 @@
from django.views import static
from django.contrib import messages
+from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from account.models import EmailAddress
@@ -203,8 +204,47 @@ def proposal_detail(request, pk):
if request.user not in [p.user for p in proposal.speakers()]:
raise Http404()
+ if "symposion.reviews" in settings.INSTALLED_APPS:
+ from symposion.reviews.forms import SpeakerCommentForm
+ message_form = SpeakerCommentForm()
+ if request.method == "POST":
+ message_form = SpeakerCommentForm(request.POST)
+ if message_form.is_valid():
+
+ message = message_form.save(commit=False)
+ message.user = request.user
+ message.proposal = proposal
+ message.save()
+
+ ProposalMessage = SpeakerCommentForm.Meta.model
+ reviewers = User.objects.filter(
+ id__in=ProposalMessage.objects.filter(
+ proposal=proposal
+ ).exclude(
+ user=request.user
+ ).distinct().values_list("user", flat=True)
+ )
+
+ for reviewer in reviewers:
+ ctx = {
+ "proposal": proposal,
+ "message": message,
+ "reviewer": True,
+ }
+ send_email(
+ [reviewer.email], "proposal_new_message",
+ context=ctx
+ )
+
+ return redirect(request.path)
+ else:
+ message_form = SpeakerCommentForm()
+ else:
+ message_form = None
+
return render(request, "proposals/proposal_detail.html", {
"proposal": proposal,
+ "message_form": message_form
})
@@ -243,7 +283,7 @@ def proposal_leave(request, pk):
proposal.additional_speakers.remove(speaker)
# @@@ fire off email to submitter and other speakers
messages.success(request, "You are no longer speaking on %s" % proposal.title)
- return redirect("speaker_dashboard")
+ return redirect("dashboard")
ctx = {
"proposal": proposal,
}
@@ -282,6 +322,9 @@ def document_create(request, proposal_pk):
proposal = get_object_or_404(queryset, pk=proposal_pk)
proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
+ if proposal.cancelled:
+ return HttpResponseForbidden()
+
if request.method == "POST":
form = SupportingDocumentCreateForm(request.POST, request.FILES)
if form.is_valid():
@@ -302,7 +345,7 @@ def document_create(request, proposal_pk):
@login_required
def document_download(request, pk, *args):
document = get_object_or_404(SupportingDocument, pk=pk)
- if settings.USE_X_ACCEL_REDIRECT:
+ if getattr(settings, "USE_X_ACCEL_REDIRECT", False):
response = HttpResponse()
response["X-Accel-Redirect"] = document.file.url
# delete content-type to allow Gondor to determine the filetype and
View
9 symposion/reviews/__init__.py
@@ -1,9 +0,0 @@
-REVIEWERS = "reviewers"
-REVIEWERS_ADMINS = "reviewers-admins"
-REVIEWERS_TUTORIALS = "reviewers-tutorials"
-
-AUTH_GROUPS = [
- REVIEWERS,
- REVIEWERS_ADMINS,
- REVIEWERS_TUTORIALS
-]
View
4 symposion/reviews/models.py
@@ -166,6 +166,10 @@ def css_class(self):
self.VOTES.MINUS_ZERO: "minus-zero",
self.VOTES.MINUS_ONE: "minus-one",
}[self.vote]
+
+ @property
+ def section(self):
+ return self.proposal.kind.section.slug
class LatestVote(models.Model):
View
10 symposion/reviews/urls.py
@@ -1,19 +1,17 @@
-from django.conf.urls.defaults import patterns, url, include
+from django.conf.urls.defaults import patterns, url
urlpatterns = patterns("symposion.reviews.views",
- url(r"^$", "review_list", name="review_list"),
url(r"^section/(?P<section_slug>[\w\-]+)/$", "review_section", name="review_section"),
url(r"^section/(?P<section_slug>[\w\-]+)/assignments/$", "review_section", {"assigned": True}, name="review_section_assignments"),
url(r"^section/(?P<section_slug>[\w\-]+)/stats/$", "review_stats", name="review_stats"),
url(r"^section/(?P<section_slug>[\w\-]+)/stats/(?P<key>[\w]+)/$", "review_stats", name="review_stats"),
-
+ url(r"^section/(?P<section_slug>[\w\-]+)/list/(?P<user_pk>\d+)/$", "review_list", name="review_list_user"),
+ url(r"^section/(?P<section_slug>[\w\-]+)/admin/$", "review_admin", name="review_admin"),
+ url(r"^section/(?P<section_slug>[\w\-]+)/admin/accept/$", "review_bulk_accept", name="review_bulk_accept"),
url(r"^review/(?P<pk>\d+)/$", "review_detail", name="review_detail"),
- url(r"^list/(?P<username>[\w\-]+)/$", "review_list", name="review_list_user"),
- url(r"^admin/$", "review_admin", name="review_admin"),
- url(r"^admin/accept/$", "review_bulk_accept", name="review_bulk_accept"),
url(r"^(?P<pk>\d+)/delete/$", "review_delete", name="review_delete"),
url(r"^assignments/$", "review_assignments", name="review_assignments"),
url(r"^assignment/(?P<pk>\d+)/opt-out/$", "review_assignment_opt_out", name="review_assignment_opt_out"),
View
140 symposion/reviews/views.py
@@ -1,13 +1,14 @@
+from django.db.models import Q
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from django.contrib.auth.decorators import login_required
-from django.contrib.auth.models import User
from symposion.proposals.models import ProposalBase, ProposalSection
-from symposion.reviews.forms import ReviewForm, ReviewCommentForm, SpeakerCommentForm
+from symposion.reviews.forms import ReviewForm, SpeakerCommentForm
from symposion.reviews.forms import BulkPresentationForm
-from symposion.reviews.models import ReviewAssignment, Review, LatestVote, VOTES
+from symposion.reviews.models import ReviewAssignment, Review, LatestVote, ProposalResult
+from symposion.teams.models import Team
from symposion.utils.mail import send_email
@@ -15,14 +16,19 @@ def access_not_permitted(request):
return render(request, "reviews/access_not_permitted.html")
-def proposals_generator(request, queryset, username=None, check_speaker=True):
+def proposals_generator(request, queryset, user_pk=None, check_speaker=True):
+
for obj in queryset:
# @@@ this sucks; we can do better
if check_speaker:
if request.user in [s.user for s in obj.speakers()]:
continue
- if obj.result is None:
+
+ try:
+ obj.result
+ except ProposalResult.DoesNotExist:
continue
+
obj.comment_count = obj.result.comment_count
obj.total_votes = obj.result.vote_count
obj.plus_one = obj.result.plus_one
@@ -30,28 +36,20 @@ def proposals_generator(request, queryset, username=None, check_speaker=True):
obj.minus_zero = obj.result.minus_zero
obj.minus_one = obj.result.minus_one
lookup_params = dict(proposal=obj)
- if username:
- lookup_params["user__username"] = username
+
+ if user_pk:
+ lookup_params["user__pk"] = user_pk
else:
lookup_params["user"] = request.user
+
try:
obj.latest_vote = LatestVote.objects.get(**lookup_params).css_class()
except LatestVote.DoesNotExist:
obj.latest_vote = "no-vote"
+
yield obj
-def group_proposals(proposals):
- grouped = {}
- for proposal in proposals:
- kind = proposal.kind
- if kind in grouped:
- grouped[kind].append(proposal)
- else:
- grouped[kind] = [proposal]
- return grouped
-
-
@login_required
def review_section(request, section_slug, assigned=False):
@@ -64,79 +62,82 @@ def review_section(request, section_slug, assigned=False):
if assigned:
assignments = ReviewAssignment.objects.filter(user=request.user).values_list("proposal__id")
queryset = queryset.filter(id__in=assignments)
+
queryset = queryset.select_related("result").select_subclasses()
+
proposals = proposals_generator(request, queryset)
+
ctx = {
"proposals": proposals,
"section": section,
}
+
return render(request, "reviews/review_list.html", ctx)
@login_required
-def review_list(request, username=None):
-
- if username:
- # if they're not a reviewer admin and they aren't the person whose
- # review list is being asked for, don't let them in
- if not request.user.groups.filter(name="reviewers-admins").exists():
- if not request.user.username == username:
- return access_not_permitted(request)
- else:
- if not request.user.groups.filter(name="reviewers").exists():
+def review_list(request, section_slug, user_pk):
+
+ # if they're not a reviewer admin and they aren't the person whose
+ # review list is being asked for, don't let them in
+ if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
+ if not request.user.pk == user_pk:
return access_not_permitted(request)
queryset = ProposalBase.objects.select_related("speaker__user", "result")
- if username:
- reviewed = LatestVote.objects.filter(user__username=username).values_list("proposal", flat=True)
- queryset = queryset.filter(pk__in=reviewed)
- queryset = queryset.order_by("submitted")
+ reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list("proposal", flat=True)
+ queryset = queryset.filter(pk__in=reviewed)
+ proposals = queryset.order_by("submitted")
- # filter out tutorials for now
- queryset = queryset.exclude(kind__name__iexact="tutorial")
+ admin = request.user.has_perm("reviews.can_manage_%s" % section_slug)
- admin = request.user.groups.filter(name="reviewers-admins").exists()
+ proposals = proposals_generator(request, proposals, user_pk=user_pk, check_speaker=not admin)
- proposals = group_proposals(proposals_generator(request, queryset, username=username, check_speaker=not admin))
- rated_proposals = queryset.filter(reviews__user=request.user)
-
ctx = {
"proposals": proposals,
- "rated_proposals": rated_proposals,
- "username": username,
}
return render(request, "reviews/review_list.html", ctx)
@login_required
-def review_admin(request):
+def review_admin(request, section_slug):
- if not request.user.groups.filter(name="reviewers-admins").exists():
+ if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
return access_not_permitted(request)
def reviewers():
- queryset = User.objects.distinct().filter(groups__name="reviewers")
- for obj in queryset:
- obj.comment_count = Review.objects.filter(user=obj).count()
- obj.total_votes = LatestVote.objects.filter(user=obj).count()
- obj.plus_one = LatestVote.objects.filter(
- user = obj,
- vote = LatestVote.VOTES.PLUS_ONE
- ).count()
- obj.plus_zero = LatestVote.objects.filter(
- user = obj,
- vote = LatestVote.VOTES.PLUS_ZERO
- ).count()
- obj.minus_zero = LatestVote.objects.filter(
- user = obj,
- vote = LatestVote.VOTES.MINUS_ZERO
- ).count()
- obj.minus_one = LatestVote.objects.filter(
- user = obj,
- vote = LatestVote.VOTES.MINUS_ONE
- ).count()
- yield obj
+ already_seen = set()
+
+ for team in Team.objects.filter(permissions__codename="can_review_%s" % section_slug):
+ for membership in team.memberships.filter(Q(state="member") | Q(state="manager")):
+ user = membership.user
+ if user.pk in already_seen:
+ continue
+ already_seen.add(user.pk)
+
+ user.comment_count = Review.objects.filter(user=user).count()
+ user.total_votes = LatestVote.objects.filter(user=user).count()
+ user.plus_one = LatestVote.objects.filter(
+ user = user,
+ vote = LatestVote.VOTES.PLUS_ONE
+ ).count()
+ user.plus_zero = LatestVote.objects.filter(
+ user = user,
+ vote = LatestVote.VOTES.PLUS_ZERO
+ ).count()
+ user.minus_zero = LatestVote.objects.filter(
+ user = user,
+ vote = LatestVote.VOTES.MINUS_ZERO
+ ).count()
+ user.minus_one = LatestVote.objects.filter(
+ user = user,
+ vote = LatestVote.VOTES.MINUS_ONE
+ ).count()
+
+ yield user
+
ctx = {
+ "section_slug": section_slug,
"reviewers": reviewers(),
}
return render(request, "reviews/review_admin.html", ctx)
@@ -258,10 +259,15 @@ def review_detail(request, pk):
@login_required
@require_POST
def review_delete(request, pk):
- if not request.user.groups.filter(name="reviewers-admins").exists():
+ review = get_object_or_404(Review, pk=pk)
+ section_slug = review.section.slug
+
+ if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
return access_not_permitted(request)
+
review = get_object_or_404(Review, pk=pk)
review.delete()
+
return redirect("review_detail", pk=review.proposal.pk)
@@ -287,7 +293,7 @@ def review_stats(request, section_slug=None, key=None):
"controversial": queryset.filter(result__plus_one__gt=0, result__minus_one__gt=0).order_by("-result__vote_count"),
}
- admin = request.user.groups.filter(name="reviewers-admins").exists()
+ admin = request.user.has_perm("reviews.can_manage_%s" % section_slug)
if key:
ctx.update({
@@ -329,8 +335,8 @@ def review_assignment_opt_out(request, pk):
@login_required
-def review_bulk_accept(request):
- if not request.user.groups.filter(name="reviewers-admins").exists():
+def review_bulk_accept(request, section_slug):
+ if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
return access_not_permitted(request)
if request.method == "POST":
form = BulkPresentationForm(request.POST)
View
2  symposion/teams/views.py
@@ -127,7 +127,7 @@ def team_apply(request, slug):
membership, created = Membership.objects.get_or_create(team=team, user=request.user)
membership.state = "applied"
membership.save()
- managers = [m.user for m in team.managers()]
+ managers = [m.user.email for m in team.managers()]
send_email(managers, "teams_user_applied", context={
"team": team,
"user": request.user
View
2  symposion/templates/reviews/base.html
@@ -88,13 +88,11 @@
</a>
</li>
{% endcomment %}
- {% comment %}
<li>
<a href="{% url review_stats section.section.slug %}">
{% trans "Section Stats" %}
</a>
</li>
- {% endcomment %}
{% endfor %}
</ul>
{% endblock %}
View
4 symposion/templates/reviews/review_admin.html
@@ -3,7 +3,7 @@
{% block body %}
<h1>Reviewers</h1>
- <table>
+ <table class="table table-striped">
<tr>
<th>
Reviewer
@@ -30,7 +30,7 @@
{% for reviewer in reviewers %}
<tr>
<td>
- <a href="{% url review_list_user reviewer.username %}">{{ reviewer }}</a>
+ <a href="{% url review_list_user section_slug reviewer.pk %}">{{ reviewer.get_full_name }}</a>
</td>
<td>
{{ reviewer.total_votes }}

No commit comments for this range

Something went wrong with that request. Please try again.