Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pydotorg/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
'django.contrib.staticfiles',
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.humanize',

'pipeline',
'sitetree',
Expand Down
1 change: 1 addition & 0 deletions sponsors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class SponsorshipPackageAdmin(OrderedModelAdmin):

class SponsorContactInline(admin.TabularInline):
model = SponsorContact
raw_id_fields = ["user"]
extra = 0


Expand Down
30 changes: 29 additions & 1 deletion sponsors/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from itertools import chain

from django.contrib import messages
from django.contrib.auth.models import Group
from django.conf import settings
from django.urls import reverse, reverse_lazy
from django.test import TestCase
Expand Down Expand Up @@ -43,6 +44,9 @@ def setUp(self):
self.user = baker.make(settings.AUTH_USER_MODEL, is_staff=True, is_active=True)
self.client.force_login(self.user)

self.group = Group(name="Sponsorship Preview")
self.group.save()

def test_display_template_with_form_and_context(self):
psf_package = baker.make("sponsors.SponsorshipPackage")
extra_package = baker.make("sponsors.SponsorshipPackage")
Expand Down Expand Up @@ -73,7 +77,7 @@ def test_login_required(self):

self.assertRedirects(r, redirect_url)

def test_staff_required(self):
def test_not_staff_no_group_not_allowed(self):
redirect_url = f"{settings.LOGIN_URL}?next={self.url}"
self.user.is_staff = False
self.user.save()
Expand All @@ -83,6 +87,16 @@ def test_staff_required(self):

self.assertRedirects(r, redirect_url, fetch_redirect_response=False)

def test_group_allowed(self):
redirect_url = f"{settings.LOGIN_URL}?next={self.url}"
self.user.groups.add(self.group)
self.user.save()
self.client.force_login(self.user)

r = self.client.get(self.url)

self.assertEqual(r.status_code, 200, "user in group should have access")

def test_valid_post_redirect_user_to_next_form_step_and_save_info_in_cookies(self):
package = baker.make("sponsors.SponsorshipPackage")
for benefit in self.program_1_benefits:
Expand Down Expand Up @@ -112,6 +126,20 @@ def test_populate_form_initial_with_values_from_cookie(self):

self.assertEqual(initial, r.context["form"].initial)

def test_capacity_flag(self):
psf_package = baker.make("sponsors.SponsorshipPackage")
r = self.client.get(self.url)
self.assertEqual(False, r.context["capacities_met"])

def test_capacity_flag_when_needed(self):
at_capacity_benefit = baker.make(
SponsorshipBenefit, program=self.psf, capacity=0, soft_capacity=False
)
psf_package = baker.make("sponsors.SponsorshipPackage")

r = self.client.get(self.url)
self.assertEqual(True, r.context["capacities_met"])


class NewSponsorshipApplicationViewTests(TestCase):
url = reverse_lazy("new_sponsorship_application")
Expand Down
41 changes: 35 additions & 6 deletions sponsors/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,59 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.db import transaction
from django.utils.decorators import method_decorator
from django.http import JsonResponse
from django.views.generic import ListView, FormView
from django.urls import reverse_lazy, reverse
from django.shortcuts import redirect

from .models import Sponsor, SponsorshipBenefit, SponsorshipPackage, Sponsorship
from .models import (
Sponsor,
SponsorshipBenefit,
SponsorshipPackage,
SponsorshipProgram,
Sponsorship,
)

from sponsors.forms import SponsorshiptBenefitsForm, SponsorshipApplicationForm
from sponsors import cookies


@method_decorator(staff_member_required(login_url=settings.LOGIN_URL), name="dispatch")
class SelectSponsorshipApplicationBenefitsView(FormView):
class SelectSponsorshipApplicationBenefitsView(UserPassesTestMixin, FormView):
form_class = SponsorshiptBenefitsForm
template_name = "sponsors/sponsorship_benefits_form.html"

# TODO: Remove UserPassesTestMixin when launched, also remove following methods
def test_func(self):
return (
self.request.user.is_staff
or self.request.user.groups.filter(name="Sponsorship Preview").exists()
)

def permission_denied_message(self):
msg = "New Sponsorship Application is not yet generally available, check back soon!"
messages.add_message(self.request, messages.INFO, msg)
return msg

# END TODO

def get_context_data(self, *args, **kwargs):
programs = SponsorshipProgram.objects.all()
packages = SponsorshipPackage.objects.all()
benefits_qs = SponsorshipBenefit.objects.select_related("program")
capacities_met = any(
[
any([not b.has_capacity for b in benefits_qs.filter(program=p)])
for p in programs
]
)
Comment on lines +48 to +53
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, I didn't get why we have to filter the benefits_qs by program to check if there's one of them without available capacity. Can't we refactor the code to:

Suggested change
capacities_met = any(
[
any([not b.has_capacity for b in benefits_qs.filter(program=p)])
for p in programs
]
)
capacities_met = all([b.has_capacity for b in benefits_qs])

Copy link
Member Author

Choose a reason for hiding this comment

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

I did this primarily since I anticipate that at some point we will have programs that are no longer active. a bit premature.

kwargs.update(
{
"benefit_model": SponsorshipBenefit,
"sponsorship_packages": SponsorshipPackage.objects.all(),
"sponsorship_packages": packages,
"capacities_met": capacities_met,
}
)
return super().get_context_data(*args, **kwargs)
Expand All @@ -35,7 +64,7 @@ def get_success_url(self):
if self.request.user.is_authenticated:
return reverse_lazy("new_sponsorship_application")
else:
# TODO unit test this scenario after removing staff_member_required decortor
# TODO unit test this scenario after removing UserPassesTestMixin
return settings.LOGIN_URL

def get_initial(self):
Expand Down
12 changes: 9 additions & 3 deletions static/js/sponsors/applicationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ $(document).ready(function(){
$("#clear_form_btn").click(function(){
$("#application_form").trigger("reset");
$("#application_form [class=active]").removeClass("active");
$("#cost_label").html("Select a package or customize the benefits");
$("input[name=package]").prop("checked", false);
checkboxesContainer.find(':checkbox').each(function(){
$(this).prop('checked', false);
});
$("#cost_label").html("");
});

$("input[name=package]").change(function(){
Expand Down Expand Up @@ -34,19 +38,21 @@ $(document).ready(function(){
let data = $("form").serialize();

let cost = packageInfo.attr("data-cost");
costLabel.html('Sponsorship cost is $' + cost + '.00')
costLabel.html('Sponsorship cost is $' + cost.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' USD')
});

$("input[id^=id_benefits_]").change(function(){
let benefit = this.value;
if (benefit.length == 0) return;
if (costLabel.html() != "Updating cost...") costLabel.html("Submit your application and we'll get in touch...");
if (costLabel.html() != "Updating cost...") costLabel.html("Please submit your customized sponsorship package application and we'll contact you within 2 business days.");

let active = checkboxesContainer.find('[value=' + benefit + ']').prop("checked");
if (!active) {
let packageOnlyBenefit = $(this).attr("package_only");
if (packageOnlyBenefit) $(this).attr("disabled", true);
return;
} else {
$('label[benefit_id=' + benefit + ']').addClass("active");
}


Expand Down
10 changes: 6 additions & 4 deletions templates/sponsors/new_sponsorship_application_form.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% extends "psf/full-width.html" %}
{% load boxes widget_tweaks %}
{% load humanize %}


{% block page_title %}Submit Sponsorship Information{% endblock %}
Expand All @@ -9,18 +10,19 @@
<h1>Submit Sponsorship Information</h1>

<div id="sponsorship-info-container" class="user-feedback level-general">
<p style="display: inline">We need more information to proceed with your sponsorship application.
<p>
{% if sponsorship_package %}
You selected the <b>{{ sponsorship_package.name }}</b> package {% if sponsorship_price %}costing ${{ sponsorship_price }}.00 {% endif %}and the following benefits:
You selected the <b>{{ sponsorship_package.name }}</b> package {% if sponsorship_price %}costing ${{ sponsorship_price|intcomma }} USD {% endif %}and the following benefits:
{% else %}
You selected the following benefits:
{% endif %}
</p>
<ul>
{% for benefit in sponsorship_benefits %}
<p>{{ benefit.name }}</p>
<li>{{ benefit.name }}</li>
{% endfor %}
</ul>
<p><i>Please complete the form below.</i></p>
<span class="remove"><label id="close-info-container">Close</label> | <a href="{% url 'select_sponsorship_application_benefits' %}">Back to select benefits</a></span>
</div>

Expand All @@ -37,7 +39,7 @@ <h1>Submit Sponsorship Information</h1>
<label>{{ form.sponsor.label }}</label>
{% render_field form.sponsor %}
<br/>
<span class="helptext">You have already filled previous sponsorship application forms. Select an existing sponsor or <span id="new-application-link">create a new one</span>.
<span class="helptext">Your user account is already associated with existing PSF Sponsors. Select an existing sponsor to submit this application on their behalf or <span id="new-application-link">create a new one</span>.
</p>
{% endif %}

Expand Down
6 changes: 5 additions & 1 deletion templates/sponsors/sponsorship_application_finished.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

{% block content %}
<article class="text">
<h1>Application submited!</h1>
<h1>Application submitted!</h1>
<i>
Thank you for submitting your application to sponsor the PSF ecosystem.
We will contact you within 2 business days to discuss next steps.
</i>
</article>

{% endblock content %}
21 changes: 12 additions & 9 deletions templates/sponsors/sponsorship_benefits_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
{% block content %}

<article id="sponsorship_application_container" class="text">
<h1>Sponsorship Application</h1>

<div id="sponsorship-application-box">
{% box 'sponsorship-application' %}
</div>

<hr>

<form id="application_form" action="" method="POST">
{% csrf_token %}
Expand All @@ -24,7 +29,7 @@ <h1>Sponsorship Application</h1>
</div>

<div id="cost_container">
<span id="cost_label">Select a package or customize the benefits</span>
<span id="cost_label"></span>
</div>

<div id="clear_form_btn">
Expand All @@ -34,9 +39,8 @@ <h1>Sponsorship Application</h1>
<div id="benefits_container">
<div id="benefits_icons_legend">
<div>
<span><i class="fa fa-asterisk"></i> {{ benefit_model.NEW_MESSAGE }}</span>
<span><i class="fa fa-cubes"></i> {{ benefit_model.PACKAGE_ONLY_MESSAGE }}</span>
<span><i class="fa fa-close"></i> {{ benefit_model.NO_CAPACITY_MESSAGE }}</span>
{% if capacities_met %}<span><i class="fa fa-close"></i> {{ benefit_model.NO_CAPACITY_MESSAGE }}</span>{% endif %}
</div>
</div>

Expand All @@ -47,12 +51,11 @@ <h3 class="title">{{ field.label }}</h3>
<ul id="id_{{ field.name }}">
{% for benefit in field.field.queryset %}
<li class="{% cycle '' 'highlight' %}">
<label for="id_{{field.name}}_{{ forloop.counter0 }}" title="{{ benefit.unavailability_message }}">
<label for="id_{{field.name}}_{{ forloop.counter0 }}" benefit_id="{{ benefit.id }}">
<input id="id_{{field.name}}_{{ forloop.counter0 }}" name="{{ field.name }}" type="checkbox" value="{{ benefit.id }}" {% if benefit.unavailability_message %}disabled{% endif %} {% if benefit.id in field.initial %}checked{% endif %} {% if benefit.package_only %}package_only='true'{% endif %}>
{{ benefit.name }}
{% if benefit.new %}<span class="fa fa-asterisk"></span>{% endif %}
{% if benefit.package_only %}<i class="fa fa-cubes"></i>{% endif %}
{% if not benefit.has_capacity %}<i class="fa fa-close"></i>{% endif %}
<span {% if benefit.description %}title="{{ benefit.description }}"{% endif %}>{{ benefit.name }}</span>
{% if benefit.package_only %}<i class="fa fa-cubes" title="{{ benefit_model.PACKAGE_ONLY_MESSAGE }}"></i>{% endif %}
{% if not benefit.has_capacity %}<i class="fa fa-close" title="{{ benefit_model.NO_CAPACITY_MESSAGE }}"></i>{% endif %}
</label>
</li>
{% endfor %}
Expand Down