def result_notification_prepare(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) proposal_pks = [] try: for pk in request.POST.getlist("_selected_action"): proposal_pks.append(int(pk)) except ValueError: return HttpResponseBadRequest() model = get_proposal_model_from_section_slug(section_slug) proposals = model.objects.filter(result__status=status, ) proposals = proposals.filter(pk__in=proposal_pks) notification_template_pk = request.POST.get("notification_template", "") if notification_template_pk: notification_template = NotificationTemplate.objects.get( pk=notification_template_pk) else: notification_template = None ctx = { "section_slug": section_slug, "status": status, "notification_template": notification_template, "proposals": proposals, "proposal_pks": ",".join([str(pk) for pk in proposal_pks]), } return render(request, "reviews/result_notification_prepare.html", ctx)
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) reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list( "proposal", flat=True) model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.filter(pk__in=reviewed) proposals = queryset.order_by("submitted") admin = request.user.has_perm("reviews.can_manage_%s" % section_slug) proposals = proposals_list(request, proposals, user_pk=user_pk, check_speaker=not admin) ctx = { "proposals": proposals, "section_slug": section_slug, } return render(request, "reviews/review_list.html", ctx)
def result_notification_prepare(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) proposal_pks = [] try: for pk in request.POST.getlist("_selected_action"): proposal_pks.append(int(pk)) except ValueError: return HttpResponseBadRequest() model = get_proposal_model_from_section_slug(section_slug) # proposals = model.objects.filter( # result__status=status, # ) status_code = STATUS_MAP[status] proposals = model.objects.filter(overall_status=status_code) proposals = proposals.filter(pk__in=proposal_pks) notification_template_pk = request.POST.get("notification_template", "") if notification_template_pk: notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) else: notification_template = None ctx = { "section_slug": section_slug, "status": status, "notification_template": notification_template, "proposals": proposals, "proposal_pks": ",".join([str(pk) for pk in proposal_pks]), } return render(request, "reviews/result_notification_prepare.html", ctx)
def review_status(request, section_slug, key=None): if not request.user.has_perm("reviews.can_review_%s" % section_slug): return access_not_permitted(request) VOTE_THRESHOLD = settings.SYMPOSION_VOTE_THRESHOLD ctx = { "section_slug": section_slug, "vote_threshold": VOTE_THRESHOLD, } model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.filter(kind__section__slug=section_slug) queryset = queryset.exclude(cancelled=True) proposals = { # proposals with at least VOTE_THRESHOLD reviews and at least one +1 and no -1s, # sorted by the 'score' "positive": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one=0 ).order_by("-result__score"), # proposals with at least VOTE_THRESHOLD reviews and at least one -1 and no +1s, # reverse sorted by the 'score' "negative": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one__gt=0, result__plus_one=0 ).order_by("result__score"), # proposals with at least VOTE_THRESHOLD reviews and neither a +1 or a -1, sorted # by total votes (lowest first) "indifferent": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one=0, result__plus_one=0 ).order_by("result__vote_count"), # proposals with at least VOTE_THRESHOLD reviews and both a +1 and -1, sorted by # total votes (highest first) "controversial": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one__gt=0 ).order_by("-result__vote_count"), # proposals with fewer than VOTE_THRESHOLD reviews "too_few": queryset.filter(result__vote_count__lt=VOTE_THRESHOLD ).order_by("result__vote_count"), } admin = request.user.has_perm("reviews.can_manage_%s" % section_slug) for status in proposals: if key and key != status: continue proposals[status] = proposals_list(request, proposals[status], check_speaker=not admin) if key: ctx.update({ "key": key, "proposals": proposals[key], }) else: ctx["proposals"] = proposals return render(request, "reviews/review_stats.html", ctx)
def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) if not all([ k in request.POST for k in ["proposal_pks", "from_address", "subject", "body"] ]): return HttpResponseBadRequest() try: proposal_pks = [ int(pk) for pk in request.POST["proposal_pks"].split(",") ] except ValueError: return HttpResponseBadRequest() model = get_proposal_model_from_section_slug(section_slug) # proposals = model.objects.filter( # result__status=status, # ) status_code = STATUS_MAP[status] proposals = model.objects.filter(overall_status=status_code) proposals = proposals.filter(pk__in=proposal_pks) notification_template_pk = request.POST.get("notification_template", "") if notification_template_pk: notification_template = NotificationTemplate.objects.get( pk=notification_template_pk) else: notification_template = None emails = [] for proposal in proposals: rn = ResultNotification() rn.proposal = proposal rn.template = notification_template rn.to_address = proposal.speaker_email rn.from_address = request.POST["from_address"] rn.subject = request.POST["subject"] rn.body = Template(request.POST["body"]).render( Context({"proposal": proposal.notification_email_context()})) rn.save() emails.append(rn.email_args) send_mass_mail(emails) return redirect("result_notification", section_slug=section_slug, status=status)
def review_section(request, section_slug, assigned=False): """ :param request: :param section_slug: slug of the Section :param assigned: :return: """ if not request.user.has_perm("reviews.can_review_%s" % section_slug): return access_not_permitted(request) can_manage = False if request.user.has_perm("reviews.can_manage_%s" % section_slug): can_manage = True section = get_object_or_404(Section, slug=section_slug, conference=current_conference()) model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.all() if request.method == "POST" and can_manage: pk_string = request.POST.get('pk', request.POST.get('pks', '')) if pk_string != '': pk_list = [int(i) for i in pk_string.split(',')] for pk in pk_list: status = request.POST['status'] proposal = queryset.get(pk=pk) proposal.overall_status = status proposal.save() else: messages.error(request, _("Please select at least one application")) url_name = "review_section_assignments" if assigned else "review_section" return redirect( reverse(url_name, kwargs={'section_slug': section_slug})) if assigned: assignments = ReviewAssignment.objects.filter( user=request.user).values_list("proposal__id") queryset = queryset.filter(id__in=assignments) proposals = proposals_list(request, queryset) ctx = { "proposals": proposals, "section": section, "section_slug": section_slug, "can_manage": can_manage, "status_options": PyConProposal.STATUS_OPTIONS } return render(request, "reviews/review_list.html", ctx)
def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) if not all([k in request.POST for k in ["proposal_pks", "from_address", "subject", "body"]]): return HttpResponseBadRequest() try: proposal_pks = [int(pk) for pk in request.POST["proposal_pks"].split(",")] except ValueError: return HttpResponseBadRequest() model = get_proposal_model_from_section_slug(section_slug) # proposals = model.objects.filter( # result__status=status, # ) status_code = STATUS_MAP[status] proposals = model.objects.filter(overall_status=status_code) proposals = proposals.filter(pk__in=proposal_pks) notification_template_pk = request.POST.get("notification_template", "") if notification_template_pk: notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) else: notification_template = None emails = [] for proposal in proposals: rn = ResultNotification() rn.proposal = proposal rn.template = notification_template rn.to_address = proposal.speaker_email rn.from_address = request.POST["from_address"] rn.subject = request.POST["subject"] rn.body = Template(request.POST["body"]).render( Context({ "proposal": proposal.notification_email_context() }) ) rn.save() emails.append(rn.email_args) send_mass_mail(emails) return redirect("result_notification", section_slug=section_slug, status=status)
def result_notification(request, section_slug, status): if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) model = get_proposal_model_from_section_slug(section_slug) proposals = model.objects.filter( result__status=status).prefetch_related('notifications') notification_templates = NotificationTemplate.objects.all() ctx = { "section_slug": section_slug, "status": status, "proposals": proposals, "notification_templates": notification_templates, } return render(request, "reviews/result_notification.html", ctx)
def proposal_counts(request): model = ProposalBase proposal_type = request.GET.get('type', 'talk') if proposal_type in PROPOSAL_TYPES: try: model = get_proposal_model_from_section_slug(proposal_type + 's') except ValueError: return ({ 'error': 'unrecognized proposal type' }, 400) else: return ({ 'error': 'unrecognized proposal type' }, 400) submitted = model.objects.filter(submitted=True, cancelled=False, kind__slug=proposal_type).count() draft = model.objects.filter(submitted=False, cancelled=False, kind__slug=proposal_type).count() cancelled = model.objects.filter(cancelled=True, kind__slug=proposal_type).count() return {"submitted": submitted, "draft": draft, "cancelled": cancelled}
def review_section(request, section_slug, assigned=False): """ :param request: :param section_slug: slug of the Section :param assigned: :return: """ if not request.user.has_perm("reviews.can_review_%s" % section_slug): return access_not_permitted(request) can_manage = False if request.user.has_perm("reviews.can_manage_%s" % section_slug): can_manage = True section = get_object_or_404(Section, slug=section_slug, conference=current_conference()) model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.all() if request.method == "POST" and can_manage: pk_string = request.POST.get('pk', request.POST.get('pks', '')) if pk_string != '': pk_list = [int(i) for i in pk_string.split(',')] for pk in pk_list: status = request.POST['status'] proposal = queryset.get(pk=pk) proposal.overall_status = status proposal.save() else: messages.error(request, _("Please select at least one application")) url_name = "review_section_assignments" if assigned else "review_section" return redirect(reverse(url_name, kwargs={'section_slug': section_slug})) if assigned: assignments = ReviewAssignment.objects.filter(user=request.user).values_list("proposal__id") queryset = queryset.filter(id__in=assignments) proposals = proposals_list(request, queryset) ctx = { "proposals": proposals, "section": section, "section_slug": section_slug, "can_manage": can_manage, "status_options": PyConProposal.STATUS_OPTIONS } return render(request, "reviews/review_list.html", ctx)
def result_notification(request, section_slug, status): if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) model = get_proposal_model_from_section_slug(section_slug) # This use to be crazy and complicated and involved something # involving voting periods: #proposals = model.objects.filter(result__status=status).prefetch_related('notifications') # Now it is just driven directly by the PyCon-specific "overall" status: status_code = STATUS_MAP[status] proposals = (model.objects.filter( overall_status=status_code).prefetch_related('notifications')) notification_templates = NotificationTemplate.objects.all() ctx = { "section_slug": section_slug, "status": status, "proposals": proposals, "notification_templates": notification_templates, } return render(request, "reviews/result_notification.html", ctx)
def proposal_counts(request): model = ProposalBase proposal_type = request.GET.get('type', 'talk') if proposal_type in PROPOSAL_TYPES: try: model = get_proposal_model_from_section_slug(proposal_type + 's') except ValueError: return ({'error': 'unrecognized proposal type'}, 400) else: return ({'error': 'unrecognized proposal type'}, 400) submitted = model.objects.filter(submitted=True, cancelled=False, kind__slug=proposal_type).count() draft = model.objects.filter(submitted=False, cancelled=False, kind__slug=proposal_type).count() cancelled = model.objects.filter(cancelled=True, kind__slug=proposal_type).count() return {"submitted": submitted, "draft": draft, "cancelled": cancelled}
def result_notification(request, section_slug, status): if not request.user.has_perm("reviews.can_manage_%s" % section_slug): return access_not_permitted(request) model = get_proposal_model_from_section_slug(section_slug) # This use to be crazy and complicated and involved something # involving voting periods: #proposals = model.objects.filter(result__status=status).prefetch_related('notifications') # Now it is just driven directly by the PyCon-specific "overall" status: status_code = STATUS_MAP[status] proposals = (model.objects .filter(overall_status=status_code) .prefetch_related('notifications')) notification_templates = NotificationTemplate.objects.all() ctx = { "section_slug": section_slug, "status": status, "proposals": proposals, "notification_templates": notification_templates, } return render(request, "reviews/result_notification.html", ctx)
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) reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list("proposal", flat=True) model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.filter(pk__in=reviewed) proposals = queryset.order_by("submitted") admin = request.user.has_perm("reviews.can_manage_%s" % section_slug) proposals = proposals_list(request, proposals, user_pk=user_pk, check_speaker=not admin) ctx = { "proposals": proposals, "section_slug": section_slug, } return render(request, "reviews/review_list.html", ctx)
def review_status(request, section_slug, key=None): if not request.user.has_perm("reviews.can_review_%s" % section_slug): return access_not_permitted(request) VOTE_THRESHOLD = settings.SYMPOSION_VOTE_THRESHOLD ctx = { "section_slug": section_slug, "vote_threshold": VOTE_THRESHOLD, } model = get_proposal_model_from_section_slug(section_slug) queryset = model.objects.filter(kind__section__slug=section_slug) queryset = queryset.exclude(cancelled=True) proposals = { # proposals with at least VOTE_THRESHOLD reviews and at least one +1 and no -1s, # sorted by the 'score' "positive": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one=0).order_by("-result__score"), # proposals with at least VOTE_THRESHOLD reviews and at least one -1 and no +1s, # reverse sorted by the 'score' "negative": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one__gt=0, result__plus_one=0).order_by("result__score"), # proposals with at least VOTE_THRESHOLD reviews and neither a +1 or a -1, sorted # by total votes (lowest first) "indifferent": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one=0, result__plus_one=0).order_by("result__vote_count"), # proposals with at least VOTE_THRESHOLD reviews and both a +1 and -1, sorted by # total votes (highest first) "controversial": queryset.filter( result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one__gt=0).order_by("-result__vote_count"), # proposals with fewer than VOTE_THRESHOLD reviews "too_few": queryset.filter(result__vote_count__lt=VOTE_THRESHOLD).order_by( "result__vote_count"), } admin = request.user.has_perm("reviews.can_manage_%s" % section_slug) for status in proposals: if key and key != status: continue proposals[status] = proposals_list(request, proposals[status], check_speaker=not admin) if key: ctx.update({ "key": key, "proposals": proposals[key], }) else: ctx["proposals"] = proposals return render(request, "reviews/review_stats.html", ctx)
def proposal_list(request): """Retrieve and return a list of proposals, optionally filtered by the given acceptance status. Requires API Key. URL: /<YEAR>/pycon_api/proposals/ To filter by proposal type, add a GET query param "type" with a value of "talk", "tutorial", "lightning", or "poster", e.g.:: GET /<YEAR>/pycon_api/proposals/?type=tutorial To filter by proposal status, add a GET query param "status" with a value of "undecided", "rejected", "accepted", or "standby". So if you wanted to filter by both type and status, you might use:: GET /<YEAR>/pycon_api/proposals/?type=tutorial&status=accepted The return data, in JSON, looks like:: { 'code': 200, 'data': [<item>, <item>, ..., <item>] } where each <item> looks like:: { 'id': 13, # proposal key 'speakers': [<speaker>, <speaker>, ..., <speaker>], 'status': "undecided"|"accepted"|"rejected"|"standby" 'title': "Title of talk" } and a <speaker> looks like:: { 'name': "Speaker Name", 'email': "*****@*****.**" } """ # What model should we be pulling from? model = ProposalBase proposal_type = request.GET.get('type', 'talk') if proposal_type in PROPOSAL_TYPES: try: model = get_proposal_model_from_section_slug(proposal_type + 's') except ValueError: return ({ 'error': 'unrecognized proposal type' }, 400) else: return ({ 'error': 'unrecognized proposal type' }, 400) # See if there is such a proposal proposals = model.objects.select_related('result').order_by('pk') proposals = proposals.filter(kind__slug=proposal_type) # Don't look at unsubmitted proposals proposals = proposals.exclude(submitted=False) # Don't look at cancelled proposals. proposals = proposals.exclude(cancelled=True) # If specific proposal status is being requested, filter on that. desired_status = request.GET.get('status', None) if desired_status == 'undecided': proposals = proposals.filter(Q(result__status=desired_status) | Q(result=None)) else: proposals = proposals.filter(result__status=desired_status) # We may be asking only for ungrouped talks; if so, limit to these. ungrouped = request.GET.get('ungrouped', '').lower() in ('true', '1') if ungrouped: proposals = proposals.filter(thunderdome_group=None) # If there's a limit parameter provided, limit to those objects. if 'limit' in request.GET: proposals = proposals[0:request.GET['limit']] # Return the proposal data objects. return [i.as_dict() for i in proposals]
def proposal_list(request): """Retrieve and return a list of proposals, optionally filtered by the given acceptance status. Requires API Key. URL: /<YEAR>/pycon_api/proposals/ To filter by proposal type, add a GET query param "type" with a value of "talk", "tutorial", "lightning", or "poster", e.g.:: GET /<YEAR>/pycon_api/proposals/?type=tutorial To filter by proposal status, add a GET query param "status" with a value of "undecided", "rejected", "accepted", or "standby". So if you wanted to filter by both type and status, you might use:: GET /<YEAR>/pycon_api/proposals/?type=tutorial&status=accepted The return data, in JSON, looks like:: { 'code': 200, 'data': [<item>, <item>, ..., <item>] } where each <item> looks like:: { 'id': 13, # proposal key 'speakers': [<speaker>, <speaker>, ..., <speaker>], 'status': "undecided"|"accepted"|"rejected"|"standby" 'title': "Title of talk" } and a <speaker> looks like:: { 'name': "Speaker Name", 'email': "*****@*****.**" } """ # What model should we be pulling from? model = ProposalBase proposal_type = request.GET.get('type', 'talk') if proposal_type in PROPOSAL_TYPES: try: model = get_proposal_model_from_section_slug(proposal_type + 's') except ValueError: return ({'error': 'unrecognized proposal type'}, 400) else: return ({'error': 'unrecognized proposal type'}, 400) # See if there is such a proposal proposals = model.objects.select_related('result').order_by('pk') proposals = proposals.filter(kind__slug=proposal_type) # Don't look at unsubmitted proposals proposals = proposals.exclude(submitted=False) # Don't look at cancelled proposals. proposals = proposals.exclude(cancelled=True) # If specific proposal status is being requested, filter on that. desired_status = request.GET.get('status', None) if desired_status == 'undecided': proposals = proposals.filter( Q(result__status=desired_status) | Q(result=None)) else: proposals = proposals.filter(result__status=desired_status) # We may be asking only for ungrouped talks; if so, limit to these. ungrouped = request.GET.get('ungrouped', '').lower() in ('true', '1') if ungrouped: proposals = proposals.filter(thunderdome_group=None) # If there's a limit parameter provided, limit to those objects. if 'limit' in request.GET: proposals = proposals[0:request.GET['limit']] # Return the proposal data objects. return [i.as_dict() for i in proposals]